diff --git a/src/app/agents/page.tsx b/src/app/agents/page.tsx index 8176508e..cc86517f 100644 --- a/src/app/agents/page.tsx +++ b/src/app/agents/page.tsx @@ -93,7 +93,9 @@ export default function AgentsPage() { useEffect(() => { if (!dbDetected && hasExistingAgent && address && detectedAgentId && !cachedRef.current) { cachedRef.current = true; - cacheAgentById(address, detectedAgentId.toString()).catch(() => {}); + cacheAgentById(address, detectedAgentId.toString()).catch(() => + cacheAgentById(address, detectedAgentId.toString()).catch(() => {}), + ); } }, [dbDetected, hasExistingAgent, address, detectedAgentId]); diff --git a/src/app/api/user/agent-register/route.ts b/src/app/api/user/agent-register/route.ts index 000ac66c..6e1c38fc 100644 --- a/src/app/api/user/agent-register/route.ts +++ b/src/app/api/user/agent-register/route.ts @@ -61,12 +61,36 @@ export async function POST(request: NextRequest) { }; if (existingUser) { - await supabase.from("users").update(agentFields).eq("id", existingUser.id); + const { error: updateError } = await supabase.from("users").update(agentFields).eq("id", existingUser.id); + if (updateError) { + return NextResponse.json({ error: updateError.message }, { status: 500 }); + } } else { - await supabase.from("users").insert({ + const { error: insertError } = await supabase.from("users").insert({ primary_address: normalized, ...agentFields, }); + + // 23505: row was created concurrently — update it instead + if (insertError?.code === "23505") { + const { data: raceUser } = await supabase + .from("users") + .select("id") + .or(`primary_address.eq.${normalized},agent_wallet.eq.${normalized},agent_owner.eq.${normalized}`) + .limit(1) + .single(); + + if (raceUser) { + const { error: updateError } = await supabase.from("users").update(agentFields).eq("id", raceUser.id); + if (updateError) { + return NextResponse.json({ error: updateError.message }, { status: 500 }); + } + } else { + return NextResponse.json({ error: "Conflict but user not found on retry" }, { status: 500 }); + } + } else if (insertError) { + return NextResponse.json({ error: insertError.message }, { status: 500 }); + } } return NextResponse.json({ ok: true }); diff --git a/src/app/api/user/agent-update/route.ts b/src/app/api/user/agent-update/route.ts index 32f76f76..13a71545 100644 --- a/src/app/api/user/agent-update/route.ts +++ b/src/app/api/user/agent-update/route.ts @@ -77,7 +77,11 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: "User not found" }, { status: 404 }); } - await supabase.from("users").update(sanitized).eq("id", existingUser.id); + const { error: updateError } = await supabase.from("users").update(sanitized).eq("id", existingUser.id); + + if (updateError) { + return NextResponse.json({ error: updateError.message }, { status: 500 }); + } return NextResponse.json({ ok: true }); } catch (err) { diff --git a/src/components/AgentDashboard.tsx b/src/components/AgentDashboard.tsx index d08c3d16..955f31f5 100644 --- a/src/components/AgentDashboard.tsx +++ b/src/components/AgentDashboard.tsx @@ -103,7 +103,9 @@ export function AgentDashboard() { useEffect(() => { if (!dbDetected && isAgent && address && agentId && !cachedRef.current) { cachedRef.current = true; - cacheAgentById(address, agentId.toString()).catch(() => {}); + cacheAgentById(address, agentId.toString()).catch(() => + cacheAgentById(address, agentId.toString()).catch(() => {}), + ); } }, [dbDetected, isAgent, address, agentId]); diff --git a/src/components/AgentManage.tsx b/src/components/AgentManage.tsx index fef77471..b87918ce 100644 --- a/src/components/AgentManage.tsx +++ b/src/components/AgentManage.tsx @@ -164,21 +164,27 @@ export function AgentManage({ agentId, role }: AgentManageProps) { await publicClient.waitForTransactionReceipt({ hash }); const parsed = JSON.parse(editUri); setMetadata({ ...metadata!, ...parsed }); - setSuccessMessage("Agent profile updated"); // Persist URI update to DB - fetch("/api/user/agent-update", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - walletAddress: address, - fields: { - agent_name: parsed.name, - agent_description: parsed.description, - agent_genre: parsed.genre || null, - agent_llm_model: parsed.llmModel || null, - }, - }), - }).catch(() => {}); + try { + const cacheRes = await fetch("/api/user/agent-update", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + walletAddress: address, + fields: { + agent_name: parsed.name, + agent_description: parsed.description, + agent_genre: parsed.genre || null, + agent_llm_model: parsed.llmModel || null, + }, + }), + }); + setSuccessMessage(cacheRes.ok + ? "Agent profile updated" + : "On-chain OK, but cache failed — will sync on next visit"); + } catch { + setSuccessMessage("On-chain OK, but cache failed — will sync on next visit"); + } setEditing(false); } catch (err) { setError(err instanceof Error ? err.message : "Failed to update URI"); @@ -200,16 +206,22 @@ export function AgentManage({ agentId, role }: AgentManageProps) { }); setTxHash(hash); await publicClient.waitForTransactionReceipt({ hash }); - setSuccessMessage("Agent wallet removed"); // Persist unset to DB - fetch("/api/user/agent-update", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - walletAddress: address, - fields: { agent_wallet: null }, - }), - }).catch(() => {}); + try { + const cacheRes = await fetch("/api/user/agent-update", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + walletAddress: address, + fields: { agent_wallet: null }, + }), + }); + setSuccessMessage(cacheRes.ok + ? "Agent wallet removed" + : "On-chain OK, but cache failed — will sync on next visit"); + } catch { + setSuccessMessage("On-chain OK, but cache failed — will sync on next visit"); + } } catch (err) { setError(err instanceof Error ? err.message : "Failed to unset wallet"); } finally { @@ -260,15 +272,21 @@ export function AgentManage({ agentId, role }: AgentManageProps) { setTxHash(hash); await publicClient.waitForTransactionReceipt({ hash }); // Persist new wallet binding to DB - fetch("/api/user/agent-update", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - walletAddress: address, - fields: { agent_wallet: newWalletAddr.toLowerCase() }, - }), - }).catch(() => {}); - setSuccessMessage("Agent wallet bound successfully"); + try { + const cacheRes = await fetch("/api/user/agent-update", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + walletAddress: address, + fields: { agent_wallet: newWalletAddr.toLowerCase() }, + }), + }); + setSuccessMessage(cacheRes.ok + ? "Agent wallet bound successfully" + : "On-chain OK, but cache failed — will sync on next visit"); + } catch { + setSuccessMessage("On-chain OK, but cache failed — will sync on next visit"); + } setWalletStep(null); setChangingWallet(false); setNewWalletAddr(""); diff --git a/src/components/AgentRegister.tsx b/src/components/AgentRegister.tsx index 1d093410..eacaadba 100644 --- a/src/components/AgentRegister.tsx +++ b/src/components/AgentRegister.tsx @@ -102,19 +102,26 @@ export function AgentRegister() { setOwnerAddress(address); // Persist agent data to DB const meta = JSON.parse(agentURI); - fetch("/api/user/agent-register", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - walletAddress: address, - agentId: newAgentId?.toString(), - name: meta.name, - description: meta.description, - genre: meta.genre, - llmModel: meta.llmModel, - agentOwner: address, - }), - }).catch(() => {}); + try { + const cacheRes = await fetch("/api/user/agent-register", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + walletAddress: address, + agentId: newAgentId?.toString(), + name: meta.name, + description: meta.description, + genre: meta.genre, + llmModel: meta.llmModel, + agentOwner: address, + }), + }); + if (!cacheRes.ok) { + setError("On-chain OK, but cache failed — will sync on next visit"); + } + } catch { + setError("On-chain OK, but cache failed — will sync on next visit"); + } setStep("3a"); } catch (err) { setError(err instanceof Error ? err.message : "Registration failed");