Skip to content

feat: migrate account bootstrap to dedicated api#1648

Merged
sweetmantech merged 2 commits intotestfrom
codex/migrate-account-chat
Apr 7, 2026
Merged

feat: migrate account bootstrap to dedicated api#1648
sweetmantech merged 2 commits intotestfrom
codex/migrate-account-chat

Conversation

@arpitgupta1214
Copy link
Copy Markdown
Collaborator

@arpitgupta1214 arpitgupta1214 commented Apr 6, 2026

Summary

  • switch the user bootstrap flow from the local /api/account route to the dedicated POST /api/accounts endpoint
  • add a small client helper for the dedicated account bootstrap call and include the Privy bearer token when available
  • remove the now-unused local app/api/account/route.tsx

Verification

  • not run (chat-side helper test removed per request)

Summary by CodeRabbit

  • Refactor
    • Reorganized account management operations to streamline account creation and authentication workflows. Enhanced authentication service integration to improve system reliability and efficiency. These backend improvements maintain full compatibility with existing features and require no user action.

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
recoup-chat Ready Ready Preview Apr 6, 2026 8:30pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 6, 2026

📝 Walkthrough

Walkthrough

The /api/account endpoint was removed from the application, and its account fetch/create logic was refactored into a new client-side utility function. The useUser hook was updated to call the new utility while integrating with Privy's access token system for authentication.

Changes

Cohort / File(s) Summary
Account API Endpoint Removal
app/api/account/route.tsx
Entirely removed POST endpoint that handled account lookup by email or wallet, account creation, org assignment, and credit initialization. Deleted route configuration exports (dynamic, fetchCache, revalidate).
Account Fetch/Create Utility
lib/accounts/fetchOrCreateAccount.ts
New utility function that posts to the client API's accounts endpoint with optional email, wallet, and accessToken parameters. Includes Bearer token authorization support and response validation.
User Hook Refactor
hooks/useUser.tsx
Updated to invoke fetchOrCreateAccount directly instead of calling the removed /api/account endpoint. Integrated getAccessToken from Privy and updated response data structure mapping from nested (data.data.*) to flat (data.*) properties.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • sweetmantech

Poem

🔄 The account flow takes flight,
From API dust to utility light,
With Privy's token held tight,
Architecture blooms clean and right! ✨

🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Solid & Clean Code ⚠️ Warning PR violates SOLID principles: fetchOrCreateAccount combines request building, error handling, and response parsing without proper separation; useUser hook lacks error boundaries and stale state prevention, creating unhandled rejection risks. Add timeout protection with AbortController and rich error context to fetchOrCreateAccount; wrap async initialization in useUser with try-catch, cancellation flags, and error feedback per established codebase patterns.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/migrate-account-chat

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a92d2b1a5f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +36 to +37
const data: FetchOrCreateAccountResponse = await response.json();
return data.data;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Parse /api/accounts response using the API envelope

This helper assumes POST /api/accounts returns { data: ... }, but other consumers of the same accounts API use a { status, ... } envelope (for example, hooks/useOrgSettings.ts expects data.account and lib/chat/validateHeaders.ts checks result.status). If the bootstrap endpoint follows that contract, return data.data becomes undefined, so useUser initializes userData and profile fields from undefined and downstream flows that rely on account_id stop working. Parse the documented envelope (status + account) and treat non-success payloads as errors.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hooks/useUser.tsx`:
- Around line 121-141: The async init() inside the useEffect should be made
safe: wrap its body in try-catch to handle rejections from getAccessToken() and
fetchOrCreateAccount(), call the function with void init() to avoid an
unhandled-promise lint warning, and add a cancellation guard (e.g. let cancelled
= false; cleanup sets cancelled = true) so before each setState (setUserData,
setImage, setInstruction, setName, setOrganization, setJobTitle, setRoleType,
setCompanyName) you check !cancelled to avoid stale writes; also log or
propagate caught errors as appropriate.

In `@lib/accounts/fetchOrCreateAccount.ts`:
- Around line 20-37: The fetchOrCreateAccount function's POST to
`${getClientApiBaseUrl()}/api/accounts` needs timeout protection and richer
error context: wrap the fetch in an AbortController with a short timeout (e.g.,
5–10s) and abort on timeout, and on non-ok responses throw an Error that
includes response.status plus the response text/body and any relevant headers
(or JSON error payload) to aid debugging; also catch fetch-level exceptions and
include the original error message and the request context (email, wallet,
whether accessToken was sent) before rethrowing. Use the same pattern as in
lib/serpapi/searchImages.ts and lib/tasks/updateTask.ts when adding the
AbortController and enhanced error construction so callers of
fetchOrCreateAccount / the FetchOrCreateAccountResponse handling receive
detailed failure info.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: aac60865-fa9b-4ef3-8e71-6c7d7f6a4ce0

📥 Commits

Reviewing files that changed from the base of the PR and between 4c5c3fe and a92d2b1.

📒 Files selected for processing (3)
  • app/api/account/route.tsx
  • hooks/useUser.tsx
  • lib/accounts/fetchOrCreateAccount.ts
💤 Files with no reviewable changes (1)
  • app/api/account/route.tsx

Comment thread hooks/useUser.tsx
Comment on lines 121 to +141
useEffect(() => {
const init = async () => {
const config = {
method: "POST",
body: JSON.stringify({
email,
wallet: address,
}),
headers: {
"Content-Type": "application/json",
},
};
const response = await fetch("/api/account", config);

if (!response.ok) {
throw new Error(
`Email API request failed with status: ${response.status}`
);
}
const accessToken = await getAccessToken();
const data = await fetchOrCreateAccount({
email,
wallet: address,
accessToken,
});

const data = await response.json();
setUserData(data.data);
setImage(data.data?.image || "");
setInstruction(data.data?.instruction || "");
setName(data?.data?.name || "");
setOrganization(data?.data?.organization || "");
setJobTitle(data?.data?.job_title || "");
setRoleType(data?.data?.role_type || "");
setCompanyName(data?.data?.company_name || "");
setUserData(data);
setImage(data.image || "");
setInstruction(data.instruction || "");
setName(data.name || "");
setOrganization(data.organization || "");
setJobTitle(data.job_title || "");
setRoleType(data.role_type || "");
setCompanyName(data.company_name || "");
};
if (!email && !address) return;
init();
}, [email, address]);
}, [email, address, getAccessToken]);
Copy link
Copy Markdown
Contributor

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:

find . -type f -name "useUser.tsx" -o -name "useUser.ts"

Repository: recoupable/chat

Length of output: 78


🏁 Script executed:

cat -n ./hooks/useUser.tsx

Repository: recoupable/chat

Length of output: 5693


🏁 Script executed:

# Check for similar async patterns in useEffect hooks
rg -A 10 "useEffect.*\(\)" hooks/ --type ts --type tsx | head -100

Repository: recoupable/chat

Length of output: 86


🏁 Script executed:

rg -A 10 "useEffect" hooks/ --type typescript | head -150

Repository: recoupable/chat

Length of output: 93


🏁 Script executed:

rg -A 10 "useEffect" hooks/ | head -150

Repository: recoupable/chat

Length of output: 8832


🏁 Script executed:

# Look at useVercelChat.ts more closely to see the isCancelled pattern
sed -n '1,150p' hooks/useVercelChat.ts

Repository: recoupable/chat

Length of output: 6282


🏁 Script executed:

# Check how errors are handled in other async operations in the codebase
rg "catch\s*\{" hooks/ -A 2 | head -80

Repository: recoupable/chat

Length of output: 1171


Add error handling and cancellation guard to prevent stale state writes and unhandled rejections.

Lines 123–137: init() is called as a fire-and-forget function. If getAccessToken() or fetchOrCreateAccount() rejects, the promise rejection goes unhandled. Additionally, if either awaited call completes after the component unmounts or dependencies change, the subsequent setState calls (lines 130–137) will write stale data.

Wrap the async logic in try-catch, add a cleanup function with a cancellation flag to guard against stale writes, and explicitly mark the unhandled promise with void init():

Proposed fix
   useEffect(() => {
+    let isCancelled = false;
+
     const init = async () => {
-      const accessToken = await getAccessToken();
-      const data = await fetchOrCreateAccount({
-        email,
-        wallet: address,
-        accessToken,
-      });
-
-      setUserData(data);
-      setImage(data.image || "");
-      setInstruction(data.instruction || "");
-      setName(data.name || "");
-      setOrganization(data.organization || "");
-      setJobTitle(data.job_title || "");
-      setRoleType(data.role_type || "");
-      setCompanyName(data.company_name || "");
+      try {
+        const accessToken = await getAccessToken();
+        const data = await fetchOrCreateAccount({
+          email,
+          wallet: address,
+          accessToken,
+        });
+
+        if (isCancelled) return;
+        setUserData(data);
+        setImage(data.image || "");
+        setInstruction(data.instruction || "");
+        setName(data.name || "");
+        setOrganization(data.organization || "");
+        setJobTitle(data.job_title || "");
+        setRoleType(data.role_type || "");
+        setCompanyName(data.company_name || "");
+      } catch {
+        if (!isCancelled) {
+          toast.error("Failed to initialize account.");
+        }
+      }
     };
     if (!email && !address) return;
-    init();
+    void init();
+    return () => {
+      isCancelled = true;
+    };
   }, [email, address, getAccessToken]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/useUser.tsx` around lines 121 - 141, The async init() inside the
useEffect should be made safe: wrap its body in try-catch to handle rejections
from getAccessToken() and fetchOrCreateAccount(), call the function with void
init() to avoid an unhandled-promise lint warning, and add a cancellation guard
(e.g. let cancelled = false; cleanup sets cancelled = true) so before each
setState (setUserData, setImage, setInstruction, setName, setOrganization,
setJobTitle, setRoleType, setCompanyName) you check !cancelled to avoid stale
writes; also log or propagate caught errors as appropriate.

Comment on lines +20 to +37
const response = await fetch(`${getClientApiBaseUrl()}/api/accounts`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
},
body: JSON.stringify({
email,
wallet,
}),
});

if (!response.ok) {
throw new Error(`Account API request failed with status: ${response.status}`);
}

const data: FetchOrCreateAccountResponse = await response.json();
return data.data;
Copy link
Copy Markdown
Contributor

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:

fd -t f "fetchOrCreateAccount.ts"

Repository: recoupable/chat

Length of output: 95


🏁 Script executed:

cat -n lib/accounts/fetchOrCreateAccount.ts

Repository: recoupable/chat

Length of output: 1318


🏁 Script executed:

rg "AbortController|setTimeout.*fetch" lib/ --type ts --type tsx -B 2 -A 2

Repository: recoupable/chat

Length of output: 86


🏁 Script executed:

rg "AbortController|setTimeout.*fetch" lib/ -A 2 -B 2

Repository: recoupable/chat

Length of output: 453


🏁 Script executed:

fd -t f ".*\.ts$" lib/ | rg "fetch" | head -20

Repository: recoupable/chat

Length of output: 483


🏁 Script executed:

cat -n lib/serpapi/searchImages.ts | head -40

Repository: recoupable/chat

Length of output: 1364


🏁 Script executed:

cat -n lib/serpapi/searchImages.ts | sed -n '40,70p'

Repository: recoupable/chat

Length of output: 1102


🏁 Script executed:

cat -n lib/recoup/fetchPosts.ts

Repository: recoupable/chat

Length of output: 2021


🏁 Script executed:

cat -n lib/perplexity/fetchPerplexityApi.ts

Repository: recoupable/chat

Length of output: 1023


🏁 Script executed:

rg "fetch.*signal.*controller" lib/ -B 5 -A 5

Repository: recoupable/chat

Length of output: 41


🏁 Script executed:

cat -n lib/agent-templates/fetchAgentTemplates.ts

Repository: recoupable/chat

Length of output: 631


🏁 Script executed:

rg "response\.text\(\)" lib/ -B 3 -A 3

Repository: recoupable/chat

Length of output: 4499


Add timeout and rich error context to this bootstrap API call.

This utility lacks timeout protection and detailed error reporting. The fetch at line 20 can hang indefinitely if the API stalls, blocking bootstrap. The error at line 33 only reports HTTP status, making production debugging difficult. The codebase already establishes this pattern elsewhere (e.g., lib/serpapi/searchImages.ts, lib/tasks/updateTask.ts).

💡 Proposed fix
 export async function fetchOrCreateAccount({
@@
 }): Promise<AccountWithDetails> {
+  const controller = new AbortController();
+  const timeoutId = setTimeout(() => controller.abort(), 10_000);
+
+  let response: Response;
+  try {
-  const response = await fetch(`${getClientApiBaseUrl()}/api/accounts`, {
+    response = await fetch(`${getClientApiBaseUrl()}/api/accounts`, {
       method: "POST",
       headers: {
         "Content-Type": "application/json",
         ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
       },
       body: JSON.stringify({
         email,
         wallet,
       }),
+      signal: controller.signal,
     });
+  } finally {
+    clearTimeout(timeoutId);
+  }
 
   if (!response.ok) {
-    throw new Error(`Account API request failed with status: ${response.status}`);
+    const errorText = await response.text().catch(() => "");
+    throw new Error(
+      `Account API request failed with status: ${response.status}${
+        errorText ? ` - ${errorText}` : ""
+      }`
+    );
   }

Per coding guidelines: "For utility functions, ensure proper error handling."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/accounts/fetchOrCreateAccount.ts` around lines 20 - 37, The
fetchOrCreateAccount function's POST to `${getClientApiBaseUrl()}/api/accounts`
needs timeout protection and richer error context: wrap the fetch in an
AbortController with a short timeout (e.g., 5–10s) and abort on timeout, and on
non-ok responses throw an Error that includes response.status plus the response
text/body and any relevant headers (or JSON error payload) to aid debugging;
also catch fetch-level exceptions and include the original error message and the
request context (email, wallet, whether accessToken was sent) before rethrowing.
Use the same pattern as in lib/serpapi/searchImages.ts and
lib/tasks/updateTask.ts when adding the AbortController and enhanced error
construction so callers of fetchOrCreateAccount / the
FetchOrCreateAccountResponse handling receive detailed failure info.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 3 files

Confidence score: 2/5

  • There is a high-confidence regression risk in lib/accounts/fetchOrCreateAccount.ts: the account bootstrap behavior changed to a new API flow, but the new behavior is not exercised by tests, so breakage could slip through merge.
  • A concrete functional issue is called out in lib/accounts/fetchOrCreateAccount.ts: reading data.data instead of the shared { status, account } envelope can return undefined, which may leave users unauthenticated when /api/accounts follows the common contract.
  • Pay close attention to lib/accounts/fetchOrCreateAccount.ts - API response parsing and bootstrap-path test coverage need validation before merge.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="lib/accounts/fetchOrCreateAccount.ts">

<violation number="1" location="lib/accounts/fetchOrCreateAccount.ts:1">
P1: Custom agent: **Flag AI Slop and Fabricated Changes**

Rule 2 ("behavior-change PRs where the changed behavior is not exercised by tests") is violated: this change migrates account bootstrap to a new API flow, but verification states tests were not run and the helper test was removed, leaving a critical auth/account path unvalidated.</violation>

<violation number="2" location="lib/accounts/fetchOrCreateAccount.ts:37">
P2: Parse the `/api/accounts` response using the shared `{ status, account }` envelope (and treat non-success as errors). Returning `data.data` will yield undefined when the API follows the common contract, which leaves userData uninitialized and breaks downstream account_id usage.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

@@ -0,0 +1,38 @@
import { getClientApiBaseUrl } from "@/lib/api/getClientApiBaseUrl";
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 6, 2026

Choose a reason for hiding this comment

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

P1: Custom agent: Flag AI Slop and Fabricated Changes

Rule 2 ("behavior-change PRs where the changed behavior is not exercised by tests") is violated: this change migrates account bootstrap to a new API flow, but verification states tests were not run and the helper test was removed, leaving a critical auth/account path unvalidated.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/accounts/fetchOrCreateAccount.ts, line 1:

<comment>Rule 2 ("behavior-change PRs where the changed behavior is not exercised by tests") is violated: this change migrates account bootstrap to a new API flow, but verification states tests were not run and the helper test was removed, leaving a critical auth/account path unvalidated.</comment>

<file context>
@@ -0,0 +1,38 @@
+import { getClientApiBaseUrl } from "@/lib/api/getClientApiBaseUrl";
+import type { AccountWithDetails } from "@/lib/supabase/accounts/getAccountWithDetails";
+
</file context>
Fix with Cubic

}

const data: FetchOrCreateAccountResponse = await response.json();
return data.data;
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 6, 2026

Choose a reason for hiding this comment

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

P2: Parse the /api/accounts response using the shared { status, account } envelope (and treat non-success as errors). Returning data.data will yield undefined when the API follows the common contract, which leaves userData uninitialized and breaks downstream account_id usage.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/accounts/fetchOrCreateAccount.ts, line 37:

<comment>Parse the `/api/accounts` response using the shared `{ status, account }` envelope (and treat non-success as errors). Returning `data.data` will yield undefined when the API follows the common contract, which leaves userData uninitialized and breaks downstream account_id usage.</comment>

<file context>
@@ -0,0 +1,38 @@
+  }
+
+  const data: FetchOrCreateAccountResponse = await response.json();
+  return data.data;
+}
</file context>
Fix with Cubic

@arpitgupta1214
Copy link
Copy Markdown
Collaborator Author

arpitgupta1214 commented Apr 6, 2026

Successful live verification on the deployed preview:

  • Preview tested: https://recoup-chat-git-codex-migrate-account-chat-recoupable-ad724970.vercel.app
  • Verified after login in the preview with DevTools MCP
  • The migrated account bootstrap path is firing against the dedicated API: POST https://test-recoup-api.vercel.app/api/accounts
  • Observed multiple successful responses (200) during authenticated app bootstrap
  • Confirmed the live response envelope is {"data": {...}}, so the helper return data.data matches the deployed API behavior
  • The preview successfully reached the authenticated chat UI after bootstrap, so the /api/account -> /api/accounts migration is working end-to-end in the deployed PR preview

@sweetmantech sweetmantech merged commit 082d164 into test Apr 7, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants