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
15 changes: 14 additions & 1 deletion apps/http-backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
import dotenv from "dotenv";
import { fileURLToPath } from "url";
import { dirname, join } from "path";

// Load .env file FIRST, before any other imports
// This ensures our environment variables override any from other packages
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const envPath = join(__dirname, "../.env");
dotenv.config({ path: envPath, override: true });
console.log("📁 Loaded .env from:", envPath);
console.log("🔐 GOOGLE_REDIRECT_URI:", process.env.GOOGLE_REDIRECT_URI || "NOT SET");

import cookieParser from 'cookie-parser'
import { NodeRegistry } from "@repo/nodes/nodeClient";
import express from "express";
Expand All @@ -20,7 +33,7 @@ app.use(cookieParser());

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

const PORT= 3002

Expand Down
138 changes: 124 additions & 14 deletions apps/http-backend/src/routes/google_callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@ import { GoogleOAuthService } from "@repo/nodes";
import type { OAuthTokens } from "@repo/nodes";
import { AuthRequest, userMiddleware } from "./userRoutes/userMiddleware.js";
import { Router, Request, Response } from "express";
import dotenv from "dotenv";
import { fileURLToPath } from "url";
import { dirname, join } from "path";

// Get the directory of this file
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Load environment variables from the http-backend directory
// When running from dist/, we need to go up to the src directory, then to the root
// Use override: true to ensure this .env file takes precedence over others
const envPath = join(__dirname, "../../.env");
dotenv.config({ path: envPath, override: true });

// Log OAuth configuration at module load
const REDIRECT_URI = process.env.GOOGLE_REDIRECT_URI || "http://localhost:3002/oauth/google/callback";
console.log("🔐 OAuth Configuration loaded:");
console.log(" .env path:", envPath);
console.log(" GOOGLE_REDIRECT_URI from env:", process.env.GOOGLE_REDIRECT_URI || "NOT SET (using default)");
console.log(" Using Redirect URI:", REDIRECT_URI);
console.log(" GOOGLE_CLIENT_ID:", process.env.GOOGLE_CLIENT_ID ? `${process.env.GOOGLE_CLIENT_ID.substring(0, 20)}...` : "NOT SET");
console.log(" GOOGLE_CLIENT_SECRET:", process.env.GOOGLE_CLIENT_SECRET ? "SET" : "NOT SET");

export const googleAuth: Router = Router();
googleAuth.get(
Expand All @@ -11,12 +33,22 @@ googleAuth.get(
async (req: AuthRequest, res: Response) => {
// const userId = req.user?.id || "test_user"; // Get from auth middleware
const userId = req.user?.id;
const workflowId = req.query.workflowId as string | undefined;

// Ensure redirect URI matches Google Cloud Console configuration
const redirectUri = REDIRECT_URI;

console.log("🔐 OAuth Initiate - Redirect URI:", redirectUri);
console.log("🔐 OAuth Initiate - User ID:", userId);
console.log("🔐 OAuth Initiate - Workflow ID:", workflowId || "NOT PROVIDED");

// Encode userId and workflowId in state (format: userId|workflowId)
const state = workflowId ? `${userId}|${workflowId}` : userId;
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

If userId is undefined (which can happen if authentication middleware fails), the state will be 'undefined' as a string. This creates an invalid state parameter that will fail to parse properly in the callback. Add validation to ensure userId exists before proceeding with OAuth initiation.

Copilot uses AI. Check for mistakes.

const oauth2 = new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_REDIRECT_URI ||
"http://localhost:3002/auth/google/callback"
redirectUri
);

const authUrl = oauth2.generateAuthUrl({
Expand All @@ -27,10 +59,11 @@ googleAuth.get(
"https://www.googleapis.com/auth/gmail.send",
"https://www.googleapis.com/auth/gmail.readonly",
],
state: userId,
state: state,
prompt: "consent",
});

console.log("🔐 OAuth Initiate - Generated Auth URL:", authUrl);
return res.redirect(authUrl);
}
);
Expand All @@ -46,11 +79,29 @@ googleAuth.get(
return res.json({ error: "Missing or invalid authorization code" });
}

// Ensure redirect URI matches Google Cloud Console configuration
const redirectUri = REDIRECT_URI;

// Parse state: format is "userId" or "userId|workflowId"
let userId: string | undefined;
let workflowId: string | undefined;

if (state && typeof state === "string") {
const parts = state.split("|");
userId = parts[0];
workflowId = parts[1]; // Will be undefined if not provided
}

Comment on lines +88 to +94
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

If state is undefined or not a string, userId remains undefined, but the code proceeds to use it without validation. This could cause the OAuth callback to fail silently when credentials cannot be saved. Add validation and error handling when userId is not available from the state parameter.

Suggested change
if (state && typeof state === "string") {
const parts = state.split("|");
userId = parts[0];
workflowId = parts[1]; // Will be undefined if not provided
}
// Validate state parameter
if (!state || typeof state !== "string") {
console.error("🔐 OAuth Callback - Missing or invalid state parameter:", state);
return res.status(400).json({ error: "Missing or invalid state parameter" });
}
const parts = state.split("|");
userId = parts[0] || undefined;
workflowId = parts[1]; // Will be undefined if not provided
if (!userId) {
console.error("🔐 OAuth Callback - Missing userId in state parameter:", state);
return res.status(400).json({ error: "Missing user identifier in state parameter" });
}

Copilot uses AI. Check for mistakes.
console.log("🔐 OAuth Callback - Redirect URI:", redirectUri);
console.log("🔐 OAuth Callback - Received code:", code ? "✅ Present" : "❌ Missing");
console.log("🔐 OAuth Callback - State:", state);
console.log("🔐 OAuth Callback - Parsed User ID:", userId);
console.log("🔐 OAuth Callback - Parsed Workflow ID:", workflowId || "NOT PROVIDED");

const oauth2 = new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_REDIRECT_URI ||
"http://localhost:3002/auth/google/callback"
redirectUri
);

try {
Expand All @@ -69,23 +120,82 @@ googleAuth.get(
console.warn(' To force new refresh_token, user needs to revoke access at: https://myaccount.google.com/permissions');
}

// Save tokens to database if userId (state) is provided
if (state && typeof state === 'string') {
console.log(' Saving tokens for userId:', state);
await Oauth.saveCredentials(state, tokens as OAuthTokens)
// Save tokens to database if userId is provided
if (userId) {
console.log(' Saving tokens for userId:', userId);
await Oauth.saveCredentials(userId, tokens as OAuthTokens)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

Avoid automated semicolon insertion (97% of all statements in the enclosing function have an explicit semicolon).

Suggested change
await Oauth.saveCredentials(userId, tokens as OAuthTokens)
await Oauth.saveCredentials(userId, tokens as OAuthTokens);

Copilot uses AI. Check for mistakes.
console.log(' ✅ Tokens saved to database');
}

// Redirect to success page
return res.redirect("http://localhost:3000/workflow");
// Redirect to workflow page if workflowId is provided, otherwise to general workflow page
const redirectUrl = workflowId
? `http://localhost:3000/workflows/${workflowId}`
: "http://localhost:3000/workflow";
console.log(' Redirecting to:', redirectUrl);
return res.redirect(redirectUrl);
} catch (err: any) {
console.error("Google token exchange error:", err);
return res.redirect(
`http://localhost:3000/workflow?google=error&msg=${encodeURIComponent(err?.message ?? "Token exchange failed")}`
);
// Parse state to get workflowId for error redirect
let workflowId: string | undefined;
if (state && typeof state === "string") {
const parts = state.split("|");
workflowId = parts[1];
}
Comment on lines +138 to +143
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

The variable workflowId is re-declared in the catch block even though it was already declared in the outer scope at line 87. This shadowing is unnecessary and can be confusing. Remove the duplicate declaration and use the existing variable.

Suggested change
// Parse state to get workflowId for error redirect
let workflowId: string | undefined;
if (state && typeof state === "string") {
const parts = state.split("|");
workflowId = parts[1];
}

Copilot uses AI. Check for mistakes.
const errorUrl = workflowId
? `http://localhost:3000/workflows/${workflowId}?google=error&msg=${encodeURIComponent(err?.message ?? "Token exchange failed")}`
: `http://localhost:3000/workflow?google=error&msg=${encodeURIComponent(err?.message ?? "Token exchange failed")}`;
return res.redirect(errorUrl);
Comment on lines +131 to +147
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

Hardcoded frontend URLs and variable shadowing.

Two issues:

  1. Hardcoded URLs: http://localhost:3000 won't work in production. Use an environment variable like FRONTEND_URL.

  2. Variable shadowing: workflowId is re-declared on line 139 but was already declared on line 87 and is in scope. This causes the outer variable to be shadowed and could lead to confusion.

Suggested fix
+const FRONTEND_URL = process.env.FRONTEND_URL || "http://localhost:3000";
+
       // Redirect to workflow page if workflowId is provided, otherwise to general workflow page
       const redirectUrl = workflowId 
-        ? `http://localhost:3000/workflows/${workflowId}`
-        : "http://localhost:3000/workflow";
+        ? `${FRONTEND_URL}/workflows/${workflowId}`
+        : `${FRONTEND_URL}/workflow`;
       console.log('   Redirecting to:', redirectUrl);
       return res.redirect(redirectUrl);
     } catch (err: any) {
       console.error("Google token exchange error:", err);
-      // Parse state to get workflowId for error redirect
-      let workflowId: string | undefined;
-      if (state && typeof state === "string") {
-        const parts = state.split("|");
-        workflowId = parts[1];
-      }
+      // workflowId is already parsed above, no need to re-parse
       const errorUrl = workflowId
-        ? `http://localhost:3000/workflows/${workflowId}?google=error&msg=${encodeURIComponent(err?.message ?? "Token exchange failed")}`
-        : `http://localhost:3000/workflow?google=error&msg=${encodeURIComponent(err?.message ?? "Token exchange failed")}`;
+        ? `${FRONTEND_URL}/workflows/${workflowId}?google=error&msg=${encodeURIComponent(err?.message ?? "Token exchange failed")}`
+        : `${FRONTEND_URL}/workflow?google=error&msg=${encodeURIComponent(err?.message ?? "Token exchange failed")}`;
       return res.redirect(errorUrl);
     }
🤖 Prompt for AI Agents
In `@apps/http-backend/src/routes/google_callback.ts` around lines 131 - 147,
Replace the hardcoded frontend base URL with an environment-driven value (e.g.
process.env.FRONTEND_URL) when building redirectUrl and errorUrl in
google_callback.ts so production deployments use the correct host; also stop
redeclaring workflowId inside the catch block (remove the inner "let workflowId"
and assign to the existing workflowId variable parsed from state) so you don't
shadow the outer variable—use the existing workflowId plus
encodeURIComponent(err?.message ?? "Token exchange failed") when constructing
the errorUrl.

}
})

// Debug endpoint to check OAuth configuration
googleAuth.get('/debug/config', async(req: Request, res: Response)=>{
try {
const redirectUri = REDIRECT_URI;
const clientId = process.env.GOOGLE_CLIENT_ID;
const hasClientSecret = !!process.env.GOOGLE_CLIENT_SECRET;

// Create a test OAuth2 client to verify configuration
const oauth2 = new google.auth.OAuth2(
clientId,
process.env.GOOGLE_CLIENT_SECRET,
redirectUri
);

const testAuthUrl = oauth2.generateAuthUrl({
access_type: "offline",
scope: ["https://www.googleapis.com/auth/spreadsheets"],
state: "test",
prompt: "consent",
});

const urlObj = new URL(testAuthUrl);
const redirectUriInUrl = urlObj.searchParams.get('redirect_uri');

return res.json({
environment: {
GOOGLE_REDIRECT_URI: redirectUri,
GOOGLE_CLIENT_ID: clientId ? `${clientId.substring(0, 20)}...` : "NOT SET",
GOOGLE_CLIENT_SECRET: hasClientSecret ? "SET" : "NOT SET",
},
oauth2Client: {
redirectUri: redirectUri,
redirectUriInGeneratedUrl: redirectUriInUrl,
matches: redirectUri === redirectUriInUrl,
},
testAuthUrl: testAuthUrl,
message: redirectUri === redirectUriInUrl
? "✅ Configuration looks correct!"
: "❌ Redirect URI mismatch detected!"
});
} catch (err) {
return res.status(500).json({
error: err instanceof Error ? err.message : 'Unknown error',
stack: err instanceof Error ? err.stack : undefined
});
}
});
Comment on lines +151 to +197
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

Debug endpoint exposes sensitive OAuth configuration without authentication.

The /debug/config endpoint is not protected by userMiddleware and exposes OAuth client IDs and configuration details. This is a security risk in production.

Suggested fix - protect or disable in production
 // Debug endpoint to check OAuth configuration
-googleAuth.get('/debug/config', async(req: Request, res: Response)=>{
+googleAuth.get('/debug/config', userMiddleware, async(req: AuthRequest, res: Response)=>{
+    // Only allow in development
+    if (process.env.NODE_ENV === 'production') {
+        return res.status(403).json({ error: 'Debug endpoints disabled in production' });
+    }
     try {
📝 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
// Debug endpoint to check OAuth configuration
googleAuth.get('/debug/config', async(req: Request, res: Response)=>{
try {
const redirectUri = REDIRECT_URI;
const clientId = process.env.GOOGLE_CLIENT_ID;
const hasClientSecret = !!process.env.GOOGLE_CLIENT_SECRET;
// Create a test OAuth2 client to verify configuration
const oauth2 = new google.auth.OAuth2(
clientId,
process.env.GOOGLE_CLIENT_SECRET,
redirectUri
);
const testAuthUrl = oauth2.generateAuthUrl({
access_type: "offline",
scope: ["https://www.googleapis.com/auth/spreadsheets"],
state: "test",
prompt: "consent",
});
const urlObj = new URL(testAuthUrl);
const redirectUriInUrl = urlObj.searchParams.get('redirect_uri');
return res.json({
environment: {
GOOGLE_REDIRECT_URI: redirectUri,
GOOGLE_CLIENT_ID: clientId ? `${clientId.substring(0, 20)}...` : "NOT SET",
GOOGLE_CLIENT_SECRET: hasClientSecret ? "SET" : "NOT SET",
},
oauth2Client: {
redirectUri: redirectUri,
redirectUriInGeneratedUrl: redirectUriInUrl,
matches: redirectUri === redirectUriInUrl,
},
testAuthUrl: testAuthUrl,
message: redirectUri === redirectUriInUrl
? "✅ Configuration looks correct!"
: "❌ Redirect URI mismatch detected!"
});
} catch (err) {
return res.status(500).json({
error: err instanceof Error ? err.message : 'Unknown error',
stack: err instanceof Error ? err.stack : undefined
});
}
});
// Debug endpoint to check OAuth configuration
googleAuth.get('/debug/config', userMiddleware, async(req: AuthRequest, res: Response)=>{
// Only allow in development
if (process.env.NODE_ENV === 'production') {
return res.status(403).json({ error: 'Debug endpoints disabled in production' });
}
try {
const redirectUri = REDIRECT_URI;
const clientId = process.env.GOOGLE_CLIENT_ID;
const hasClientSecret = !!process.env.GOOGLE_CLIENT_SECRET;
// Create a test OAuth2 client to verify configuration
const oauth2 = new google.auth.OAuth2(
clientId,
process.env.GOOGLE_CLIENT_SECRET,
redirectUri
);
const testAuthUrl = oauth2.generateAuthUrl({
access_type: "offline",
scope: ["https://www.googleapis.com/auth/spreadsheets"],
state: "test",
prompt: "consent",
});
const urlObj = new URL(testAuthUrl);
const redirectUriInUrl = urlObj.searchParams.get('redirect_uri');
return res.json({
environment: {
GOOGLE_REDIRECT_URI: redirectUri,
GOOGLE_CLIENT_ID: clientId ? `${clientId.substring(0, 20)}...` : "NOT SET",
GOOGLE_CLIENT_SECRET: hasClientSecret ? "SET" : "NOT SET",
},
oauth2Client: {
redirectUri: redirectUri,
redirectUriInGeneratedUrl: redirectUriInUrl,
matches: redirectUri === redirectUriInUrl,
},
testAuthUrl: testAuthUrl,
message: redirectUri === redirectUriInUrl
? "✅ Configuration looks correct!"
: "❌ Redirect URI mismatch detected!"
});
} catch (err) {
return res.status(500).json({
error: err instanceof Error ? err.message : 'Unknown error',
stack: err instanceof Error ? err.stack : undefined
});
}
});
🤖 Prompt for AI Agents
In `@apps/http-backend/src/routes/google_callback.ts` around lines 151 - 197, The
/debug/config route (googleAuth.get('/debug/config')) currently returns
sensitive OAuth info (REDIRECT_URI, GOOGLE_CLIENT_ID/SECRET, testAuthUrl)
without authentication; fix by either removing the endpoint from production or
protecting it: wrap the handler with the existing userMiddleware (or another
auth middleware) so only authenticated users can access it, or gate the entire
route behind an environment check (e.g., only register/return when NODE_ENV ===
'development' or a DEBUG_CONFIG flag) and ensure GOOGLE_CLIENT_SECRET is never
returned; update the route registration accordingly.


// Debug endpoint to check stored credentials
googleAuth.get('/debug/credentials', async(req: Request, res: Response)=>{
try {
Expand Down
33 changes: 23 additions & 10 deletions apps/http-backend/src/routes/userRoutes/userRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,20 +213,33 @@ router.get(
},
});

// if (credentials.length === 0) {
// // No credentials found - return the correct auth URL
// const authUrl = `${process.env.BACKEND_URL || "http://localhost:3002"}/oauth/google/initiate`;
// return res.status(statusCodes.OK).json({
// message:
// "Credentials not found create credentials using this auth url",
// Data: authUrl,
// });
// }

// // Credentials found - return them
// return res.status(statusCodes.OK).json({
// message: "Credentials Fetched successfully",
// Data: credentials,
// });
Comment on lines +216 to +230
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

Large block of commented-out code should be removed rather than left in the codebase. If this code needs to be preserved for reference, it should be documented in version control history or migration notes.

Suggested change
// if (credentials.length === 0) {
// // No credentials found - return the correct auth URL
// const authUrl = `${process.env.BACKEND_URL || "http://localhost:3002"}/oauth/google/initiate`;
// return res.status(statusCodes.OK).json({
// message:
// "Credentials not found create credentials using this auth url",
// Data: authUrl,
// });
// }
// // Credentials found - return them
// return res.status(statusCodes.OK).json({
// message: "Credentials Fetched successfully",
// Data: credentials,
// });

Copilot uses AI. Check for mistakes.
if (credentials.length === 0) {
// No credentials found - return the correct auth URL
const authUrl = `${process.env.BACKEND_URL || "http://localhost:3002"}/auth/google/initiate`;
return res.status(statusCodes.OK).json({
message:
"Credentials not found create credentials using this auth url",
Data: authUrl,
return res.status(200).json({
message: "No credentials found",
data: [], // always array
hasCredentials: false,
});
}

// Credentials found - return them
return res.status(statusCodes.OK).json({
message: "Credentials Fetched successfully",
Data: credentials,
return res.status(200).json({
message: "Credentials fetched",
data: credentials,
hasCredentials: true,
});
} catch (e) {
console.log(
Expand Down
47 changes: 33 additions & 14 deletions apps/web/app/hooks/useCredential.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,53 @@
"use client";
import { useEffect, useState } from "react";
import { getCredentials } from "../workflow/lib/config";
import { BACKEND_URL } from "@repo/common/zod";

export const useCredentials = (type: string, workflowId?: string): any => {
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

The return type is any, which defeats the purpose of TypeScript's type safety. Define a proper return type interface such as { cred: any[], authUrl: string | null } to provide better type checking and IDE support.

Suggested change
export const useCredentials = (type: string, workflowId?: string): any => {
interface UseCredentialsResult {
cred: any[];
authUrl: string | null;
}
export const useCredentials = (
type: string,
workflowId?: string
): UseCredentialsResult => {

Copilot uses AI. Check for mistakes.
const [cred, setCred] = useState<any[]>([]);
const [authUrl, setAuthUrl] = useState<string | null>(null);

export const useCredentials = (type: string): any => {
const [cred, setCred] = useState<any>();
const [authUrl, setAuthUrl] = useState<string>();
useEffect(() => {
const fetchCred = async () => {
try {
if (!type) return {};
// Clear credentials and authUrl when type is empty to prevent leaking credentials
if (!type) {
setCred([]);
setAuthUrl(null);
return;
}

const response = await getCredentials(type);
if (response) {
console.log(typeof response);
if (typeof response === "string") setAuthUrl(response);
else setCred(response);

// console.log(response[0].nodeId)
console.log(response);
return cred;
} else return {};
// Backend should ONLY return stored credentials
if (Array.isArray(response)) {
setCred(response);
} else {
setCred([]);
}
Comment on lines 20 to +27
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:

#!/bin/bash
# First, find and examine the getCredentials function implementation
echo "=== Searching for getCredentials function ==="
find . -name "config.ts" -path "*/workflow/lib/*" | head -5

Repository: Dev-Pross/BuildFlow

Length of output: 146


🏁 Script executed:

#!/bin/bash
# Look at the useCredential.ts file to understand the context
find . -name "useCredential.ts" | head -5

Repository: Dev-Pross/BuildFlow

Length of output: 100


🏁 Script executed:

#!/bin/bash
# Search for getCredentials definition and usage
rg "const getCredentials|export.*getCredentials" -t ts --type-add 'ts:*.ts' -B 2 -A 10

Repository: Dev-Pross/BuildFlow

Length of output: 799


🏁 Script executed:

#!/bin/bash
# Get more context from config.ts to see the complete getCredentials function
cat -n apps/web/app/workflow/lib/config.ts | grep -A 20 "export const getCredentials"

Repository: Dev-Pross/BuildFlow

Length of output: 691


🏁 Script executed:

#!/bin/bash
# Read the complete useCredential.ts file
cat -n apps/web/app/hooks/useCredential.ts

Repository: Dev-Pross/BuildFlow

Length of output: 1948


🏁 Script executed:

#!/bin/bash
# Search for backend endpoint definition or API route
find . -type f \( -name "*.ts" -o -name "*.js" -o -name "*.py" \) -path "*/backend/*" -o -path "*/server/*" -o -path "*/api/*" | head -20

Repository: Dev-Pross/BuildFlow

Length of output: 272


🏁 Script executed:

#!/bin/bash
# Search for mentions of getCredentials endpoint in the codebase
rg "getCredentials|user/getCredentials" -t ts -t js -t py -B 2 -A 5 | head -100

Repository: Dev-Pross/BuildFlow

Length of output: 6388


🏁 Script executed:

#!/bin/bash
# Look for type definitions or interfaces related to credentials response
rg "data.*Data|hasCredentials" -t ts -B 2 -A 2

Repository: Dev-Pross/BuildFlow

Length of output: 5701


Fix case mismatch in getCredentials response handling.

The backend endpoint returns { data, hasCredentials } with lowercase data, but getCredentials in apps/web/app/workflow/lib/config.ts accesses response.data.Data (uppercase). This causes the function to return undefined, making the Array.isArray(response) check in useCredential.ts fail and silently set credentials to an empty array.

Change line 40 in apps/web/app/workflow/lib/config.ts from:

return response.data.Data;

to:

return response.data.data;
🤖 Prompt for AI Agents
In `@apps/web/app/hooks/useCredential.ts` around lines 20 - 27, The getCredentials
function is returning the wrong property due to a case mismatch causing
useCredential's Array.isArray check to fail; in the getCredentials
implementation (the function named getCredentials in workflow/lib/config.ts)
change the return to use the lowercase nested field (return response.data.data)
instead of response.data.Data so that getCredentials returns the actual array
the useCredential hook expects and setCred receives the correct value.


// Frontend defines where to redirect for OAuth
if (type === "google") {
const baseUrl = `${BACKEND_URL}/oauth/google/initiate`;
const url = workflowId ? `${baseUrl}?workflowId=${workflowId}` : baseUrl;
setAuthUrl(url);
} else {
setAuthUrl(null);
}
} catch (e) {
console.log(
e instanceof Error
? e.message
: "unknow error from useCredentials hook"
: "unknown error from useCredentials hook"
);
// Clear state on error to prevent stale data
setCred([]);
setAuthUrl(null);
}
};

fetchCred();
}, [type]);
}, [type, workflowId]);

return { cred, authUrl };
};
Loading
Loading