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
9 changes: 7 additions & 2 deletions src/app/api/access/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ export async function POST(request: Request) {
try {
const supabase = getSupabaseAdminClient();
await supabase.from("users").upsert(
{ privy_id: userId, email: body.email || null, x_handle: body.xHandle || null, last_seen_at: new Date().toISOString() },
{ onConflict: "privy_id", ignoreDuplicates: false },
{
id: `privy:${userId}`,
email: body.email || null,
x_handle: body.xHandle || null,
last_seen_at: new Date().toISOString(),
},
{ onConflict: "id", ignoreDuplicates: false },
);
} catch { /* non-fatal */ }

Expand Down
40 changes: 35 additions & 5 deletions src/app/api/user/wallet/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,30 @@ async function getCodeId(): Promise<string | null> {
return verifyAccessToken(token);
}

function isPrivyUser(codeId: string) {
return codeId.startsWith("privy:");
}

export async function GET() {
const codeId = await getCodeId();
if (!codeId) return NextResponse.json({ wallet: null });

const supabase = getSupabaseAdminClient();

if (isPrivyUser(codeId)) {
const { data } = await supabase
.from("users")
.select("wallet")
.eq("id", codeId)
.maybeSingle();
return NextResponse.json({ wallet: data?.wallet ?? null });
}

const { data } = await supabase
.from("access_codes")
.select("wallet")
.eq("id", codeId)
.maybeSingle();

return NextResponse.json({ wallet: data?.wallet ?? null });
}

Expand All @@ -35,10 +48,27 @@ export async function POST(request: Request) {
}

const supabase = getSupabaseAdminClient();
await supabase
.from("access_codes")
.update({ wallet })
.eq("id", codeId);

if (isPrivyUser(codeId)) {
await supabase.from("users").update({ wallet }).eq("id", codeId);
} else {
await supabase.from("access_codes").update({ wallet }).eq("id", codeId);
}

return NextResponse.json({ ok: true });
}

export async function DELETE() {
const codeId = await getCodeId();
if (!codeId) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

const supabase = getSupabaseAdminClient();

if (isPrivyUser(codeId)) {
await supabase.from("users").update({ wallet: null }).eq("id", codeId);
} else {
await supabase.from("access_codes").update({ wallet: null }).eq("id", codeId);
}

return NextResponse.json({ ok: true });
}
9 changes: 4 additions & 5 deletions src/app/dashboard/luca/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ function LucaChat() {
}

const showEmpty = messages.length === 0;
const isReady = Boolean(selectedSlug || wallet);
const isReady = true;
const greeting = (() => {
const h = new Date().getHours();
return h < 12 ? "Good morning" : h < 18 ? "Good afternoon" : "Good evening";
Expand Down Expand Up @@ -280,8 +280,7 @@ function LucaChat() {
What should I read for you today?
</p>
<p style={{ fontSize: "0.8rem", color: "var(--muted)", maxWidth: 400, lineHeight: 1.6, margin: "0 0 32px" }}>
Luca answers from your agent&apos;s attributed books only — every figure carries its period and confidence, and missing data is called missing.
{!isReady && " Link your wallet or select an agent to get started."}
Luca answers from attributed books only — every figure carries its period and confidence, and missing data is called missing.
</p>

{/* Capability grid — 3-col, 6px radius (no pills) */}
Expand Down Expand Up @@ -434,8 +433,8 @@ function LucaChat() {
<textarea
ref={textareaRef}
className="op-chat-input"
placeholder={isReady ? "Ask Luca about your agent's finances…" : "Link your wallet or select an agent to start"}
disabled={!isReady || sending}
placeholder={selectedSlug ? `Ask Luca about ${selectedSlug}'s finances…` : "Ask Luca anything about agent finance…"}
disabled={sending}
rows={1}
value={input}
onChange={handleInputChange}
Expand Down
18 changes: 18 additions & 0 deletions supabase/migrations/20260725000003_users_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- Users table for Privy-authenticated accounts (Google/X sign-in).
-- access_codes is UUID-keyed and only supports invite-code flow.
-- This table supports all Privy-based auth, keyed by privy:<privyId>.
CREATE TABLE IF NOT EXISTS public.users (
id text PRIMARY KEY, -- privy:<privyId>
email text,
x_handle text,
wallet text,
last_seen_at timestamptz DEFAULT now(),
created_at timestamptz NOT NULL DEFAULT now()
);

ALTER TABLE public.users ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Service role can manage users"
ON public.users FOR ALL
USING (true)
WITH CHECK (true);
Loading