diff --git a/app/api/nodes/[id]/route.ts b/app/api/nodes/[id]/route.ts index 7950820..0e34c70 100644 --- a/app/api/nodes/[id]/route.ts +++ b/app/api/nodes/[id]/route.ts @@ -49,6 +49,7 @@ export async function DELETE( try { // 1. Fetch the node from SurrealDB to verify ownership and get atp_uri const db = await connectToDB(); + const nodeResult = await db.query<[Array<{ id: string; user_did: string; atp_uri: string }>]>( 'SELECT id, user_did, atp_uri FROM node WHERE id = $nodeId', { nodeId: id } diff --git a/app/api/nodes/debug/route.ts b/app/api/nodes/debug/route.ts new file mode 100644 index 0000000..1056625 --- /dev/null +++ b/app/api/nodes/debug/route.ts @@ -0,0 +1,66 @@ +import { NextResponse } from 'next/server'; +import { cookies } from 'next/headers'; +import { connectToDB } from '@/lib/db'; +import { verifySurrealJwt } from '@/lib/auth/jwt'; + +/** + * GET /api/nodes/debug + * + * Debug endpoint to list all nodes for the current user in SurrealDB. + * Only available in development mode. + */ +export async function GET() { + // Only allow in development + if (process.env.NODE_ENV !== 'development') { + return NextResponse.json({ error: 'Not available in production' }, { status: 403 }); + } + + const cookieStore = await cookies(); + const surrealJwt = cookieStore.get('ponderants-auth')?.value; + + console.log('[DEBUG /api/nodes/debug] Auth check:', { + hasSurrealJwt: !!surrealJwt, + }); + + if (!surrealJwt) { + console.error('[DEBUG /api/nodes/debug] Missing auth cookie'); + return NextResponse.json({ error: 'Not authenticated' }, { status: 401 }); + } + + // Verify the JWT and extract user info + const userSession = verifySurrealJwt(surrealJwt); + if (!userSession) { + console.error('[DEBUG /api/nodes/debug] Invalid JWT'); + return NextResponse.json({ error: 'Invalid auth token' }, { status: 401 }); + } + + const { did: userDid } = userSession; + + try { + const db = await connectToDB(); + + // Fetch ALL nodes for this user (no filters) + const nodesResult = await db.query< + [Array<{ id: string; title: string; body: string; user_did: string; atp_uri: string }>] + >('SELECT id, title, body, user_did, atp_uri FROM node WHERE user_did = $userDid', { + userDid, + }); + + const nodes = nodesResult[0] || []; + + console.log('[DEBUG /api/nodes/debug] Found nodes:', { + count: nodes.length, + userDid, + nodeIds: nodes.map((n) => n.id), + }); + + return NextResponse.json({ + nodes, + userDid, + count: nodes.length, + }); + } catch (error) { + console.error('[DEBUG /api/nodes/debug] Error:', error); + return NextResponse.json({ error: 'Failed to fetch nodes' }, { status: 500 }); + } +} diff --git a/components/UserMenu.tsx b/components/UserMenu.tsx index c8900ab..1d32e3b 100644 --- a/components/UserMenu.tsx +++ b/components/UserMenu.tsx @@ -1,8 +1,9 @@ 'use client'; import { useState, useEffect } from 'react'; -import { Menu, Avatar, NavLink, ActionIcon, SegmentedControl, Text } from '@mantine/core'; +import { Menu, Avatar, NavLink, ActionIcon, SegmentedControl, Text, Stack, ScrollArea, Code } from '@mantine/core'; import { useMantineColorScheme } from '@mantine/core'; +import { notifications } from '@mantine/notifications'; import { IconSun, IconMoon, IconDeviceDesktop } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; @@ -13,11 +14,20 @@ interface UserProfile { avatar: string | null; } +interface Node { + id: string; + title: string; + user_did: string; +} + export function UserMenu({ showLabel = false }: { showLabel?: boolean } = {}) { const router = useRouter(); const { colorScheme, setColorScheme } = useMantineColorScheme(); const [profile, setProfile] = useState(null); const [loading, setLoading] = useState(true); + const [nodes, setNodes] = useState([]); + const [nodesLoading, setNodesLoading] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); useEffect(() => { // Fetch user profile on mount @@ -36,6 +46,62 @@ export function UserMenu({ showLabel = false }: { showLabel?: boolean } = {}) { }); }, []); + // Fetch user's nodes for debugging + const fetchNodes = async () => { + setNodesLoading(true); + try { + const response = await fetch('/api/nodes/debug', { + credentials: 'include', + }); + const data = await response.json(); + if (!data.error) { + setNodes(data.nodes || []); + } + } catch (error) { + console.error('Failed to fetch nodes:', error); + } finally { + setNodesLoading(false); + } + }; + + // Delete a node (debug) - Matches ThoughtGalaxy delete pattern + const handleDebugDelete = async (nodeId: string) => { + setIsDeleting(true); + + try { + // Extract clean ID from SurrealDB RecordId format (removes angle brackets ⟨⟩) + const cleanId = String(nodeId).replace(/[⟨⟩]/g, ''); + + const response = await fetch(`/api/nodes/${cleanId}`, { + method: 'DELETE', + credentials: 'include', + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.error || 'Failed to delete node'); + } + + notifications.show({ + title: 'Node deleted', + message: 'Node has been deleted from Bluesky and your galaxy', + color: 'green', + }); + + // Update local state to remove the deleted node + setNodes((prevNodes) => prevNodes.filter((n) => n.id !== nodeId)); + } catch (error) { + console.error('[UserMenu Debug] Delete error:', error); + notifications.show({ + title: 'Delete failed', + message: error instanceof Error ? error.message : 'Failed to delete node', + color: 'red', + }); + } finally { + setIsDeleting(false); + } + }; + const handleLogout = async () => { try { await fetch('/api/auth/logout', { method: 'POST' }); @@ -173,6 +239,68 @@ export function UserMenu({ showLabel = false }: { showLabel?: boolean } = {}) { /> + {/* Debug: Show all nodes */} + {process.env.NODE_ENV === 'development' && ( + <> + + Debug: SurrealDB Nodes +
+ + + + {nodes.length > 0 && ( + + + {nodes.map((node) => ( +
+
+ {node.title} + +
+ {node.id} +
+ ))} +
+
+ )} + {nodes.length === 0 && !nodesLoading && ( + + No nodes found in SurrealDB + + )} +
+ + )} + Log out diff --git a/todo.md b/todo.md index 941c087..5b07f21 100644 --- a/todo.md +++ b/todo.md @@ -6,11 +6,15 @@ Upcoming items that should be implemented (time-permitting): playwright mcp testing as well as that of magnitude - ADD MAGNITUDE TESTS FOR EVERYTHING, both existing and new additions - stream the AI output to deepgram for faster synthesis -- fix the freaking galaxy node clicking -- when going directly to a node ID - link, it redirects to /chat; when clicking on a node in /galaxy (either - general or on a specific node ID url there), it closes the modal automatically -- dark mode/light mode favicon and overall app theme +- dark mode/light mode favicon - fix the double border on desktop between sidebar and conversation actions UI - delete "backup"/"old" page.tsx files - allow ai to transition to edit in chat -- why wait for three nodes before umap? +- fix creation/display of node links +- render markdown +- fix the "new tables being created instead of adding to the proper table" + issues we're having with the other tables like we were having with the node + table and we're now having with at least the oauth session, oauth state, and + user tabless; it's probably happening with the link_to table as well but that + one doesn't have data because it seems like link creation is broken (see task + above to fix)