From 45e7b8c6b3183a8402280bd0ef721a2ca6d13e25 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Sat, 28 Feb 2026 20:12:51 +0100 Subject: [PATCH] correct search page Nostr-Signature: 2a93ec13a9ae177dfd3f4b59cfc7341e9a3a073367b43976ea161802efc76c44 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc 7821315a6b3b5761c8938fd9b247db0f3344336fbf706b1fb5921ee5645b0f77298cb41a96cd5d1afa8e05c25eb7d64d69d42de585cdd016ad1425ca5b1f4772 --- nostr/commit-signatures.jsonl | 1 + src/routes/api/search/+server.ts | 66 +++++++++++++++++++++++++++++++- src/routes/search/+page.svelte | 63 ++++++++++++++++++++++++++++-- 3 files changed, 124 insertions(+), 6 deletions(-) diff --git a/nostr/commit-signatures.jsonl b/nostr/commit-signatures.jsonl index e7c437a..1035c08 100644 --- a/nostr/commit-signatures.jsonl +++ b/nostr/commit-signatures.jsonl @@ -129,3 +129,4 @@ {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772300935,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"3618c494d594408165ebf461e290676817ab6cc8b0b076ccc02b35a487ae8da1","sig":"d9106ce1318e703df1c366835be69c0cab12fba3a9be0fed944b17e55fd3b44f3fc9d45b32366d1d237452f099ab3b07f8ad6199660972ce571ec23ae264e873"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772302842,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"5c4b680a04363718d8de6aa05b824d30417221a9095be57bb9a7c2cf01c5af59","sig":"51ffa554e83a6a3c4ca97cffc7eca67e770ca822e43e9e78692bafcd63401c4df84e1fe030592e63982b509d3cfa8bfbd57c6b4257661b0f43adedef335c7575"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772303976,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fix"]],"content":"Signed commit: bug-fix","id":"a8e7a4f38f815abaa8cc807e43da842cc4715ff41e722ee6657cae57915b753e","sig":"9c427e839796099f8fdfc0dc4a6f4500ecb1835dbf62cf0b1d3dbe8f98c98a1bc7b28266cbc7a993a18f905ec6e57c610e10492c88e7f863319cefb2eab58fdc"} +{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772305338,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","allow forks"]],"content":"Signed commit: allow forks","id":"47f1aa9a47f4488a9babf752466bb2e4cb7974bd67aa827a4b70c57bac839750","sig":"3652f31ee120f894f7dbb04bb2e625dc2f97758b9fe99fa24aa0efe23872851aceb6f460d2c479bbfc9aa495b5c6d993b2ec7d65e21c90ee8ca6a9f23bfac498"} diff --git a/src/routes/api/search/+server.ts b/src/routes/api/search/+server.ts index 62c16f1..31f2a1c 100644 --- a/src/routes/api/search/+server.ts +++ b/src/routes/api/search/+server.ts @@ -171,8 +171,18 @@ export const GET: RequestHandler = async (event) => { const relayResult = await relayFetchPromise; relayResults = relayResult.filtered; allRelayRepos = relayResult.allRepos; + logger.debug({ + filteredCount: relayResults.length, + allReposCount: allRelayRepos.length, + query: queryTrimmed.substring(0, 50) + }, 'Relay fetch completed successfully'); } catch (err) { - logger.debug({ error: err }, 'Failed to get relay results'); + logger.warn({ + error: err instanceof Error ? err.message : String(err), + errorStack: err instanceof Error ? err.stack : undefined, + query: queryTrimmed.substring(0, 50), + cachedCount: cachedResults.length + }, 'Failed to get relay results - will return cached results if available'); } // Step 4 & 5: Deduplicate results (cached + relay) using deduplication keys @@ -252,6 +262,54 @@ export const GET: RequestHandler = async (event) => { }, 'Cache updated with filtered relay results (no unfiltered repos available)'); } + // If we have no results and cache was empty, try a simple fetch to populate cache + if (allResults.size === 0 && cachedRepos.length === 0 && allRelayRepos.length === 0) { + logger.info({ query: queryTrimmed.substring(0, 50) }, 'No results found and cache is empty - attempting simple relay fetch to populate cache'); + try { + // Try a simple fetch of recent repo announcements to populate cache + const simpleFetch = nostrClient.fetchEvents([{ + kinds: [KIND.REPO_ANNOUNCEMENT], + limit: 100 + }]).catch(err => { + logger.debug({ error: err }, 'Simple cache population fetch failed'); + return [] as NostrEvent[]; + }); + + const simpleResults = await Promise.race([ + simpleFetch, + new Promise((resolve) => { + setTimeout(() => { + logger.debug({ query: queryTrimmed.substring(0, 50) }, 'Simple cache population fetch timeout (5s)'); + resolve([]); + }, 5000); + }) + ]); + + if (simpleResults.length > 0) { + // Update cache with fetched repos + eventCache.set(cacheKey, simpleResults); + logger.info({ + fetchedCount: simpleResults.length, + query: queryTrimmed.substring(0, 50) + }, 'Cache populated with simple fetch - filtering results'); + + // Filter the newly fetched repos + const newFiltered = filterRepos(simpleResults, queryTrimmed, queryLower, resolvedPubkey, decodedAddress); + + // Add to allResults map + newFiltered.forEach(r => { + const key = getDeduplicationKey(r); + const existing = allResults.get(key); + if (!existing || r.created_at > existing.created_at) { + allResults.set(key, r); + } + }); + } + } catch (err) { + logger.debug({ error: err, query: queryTrimmed.substring(0, 50) }, 'Simple cache population failed'); + } + } + const mergedResults = Array.from(allResults.values()); logger.debug({ @@ -367,7 +425,11 @@ export const GET: RequestHandler = async (event) => { query: queryTrimmed.substring(0, 50), resultCount: limitedResults.length, totalMatches: mergedResults.length, - fromCache + cachedCount: cachedResults.length, + relayCount: relayResults.length, + fromCache, + hasCachedRepos: cachedRepos.length > 0, + hasRelayRepos: allRelayRepos.length > 0 }, 'Search completed'); return json({ diff --git a/src/routes/search/+page.svelte b/src/routes/search/+page.svelte index eeb7da2..ca0fad5 100644 --- a/src/routes/search/+page.svelte +++ b/src/routes/search/+page.svelte @@ -101,26 +101,53 @@ error = null; results = null; // Reset results + const searchQuery = query.trim(); + const searchUrl = `/api/search?q=${encodeURIComponent(searchQuery)}`; + + console.log('[Search] Starting search:', { query: searchQuery, url: searchUrl, hasUserPubkey: !!userPubkeyHex }); + try { const headers: Record = {}; if (userPubkeyHex) { headers['X-User-Pubkey'] = userPubkeyHex; } - const response = await fetch(`/api/search?q=${encodeURIComponent(query.trim())}`, { + console.log('[Search] Fetching:', { url: searchUrl, headers }); + + const response = await fetch(searchUrl, { headers, signal: currentAbortController.signal }); + console.log('[Search] Response received:', { + status: response.status, + statusText: response.statusText, + ok: response.ok, + headers: Object.fromEntries(response.headers.entries()) + }); + // Check if request was aborted if (currentAbortController.signal.aborted) { + console.log('[Search] Request was aborted'); return; } if (response.ok) { const data = await response.json(); + console.log('[Search] Response data:', { + query: data.query, + total: data.total, + reposCount: data.results?.repos?.length || 0, + fromCache: data.fromCache, + fullData: data + }); + // Verify the response matches our current query (in case of race conditions) - if (data.query !== query.trim()) { + if (data.query !== searchQuery) { + console.warn('[Search] Response query mismatch:', { + expected: searchQuery, + received: data.query + }); // Response is for a different query, ignore it return; } @@ -132,22 +159,50 @@ repos: Array.isArray(apiResults.repos) ? apiResults.repos : [], total: typeof data.total === 'number' ? data.total : (apiResults.repos?.length || 0) }; + + console.log('[Search] Search completed successfully:', { + resultCount: results.repos.length, + total: results.total + }); } else { - const data = await response.json(); - error = data.error || 'Search failed'; + let errorData; + try { + errorData = await response.json(); + } catch { + errorData = { error: `HTTP ${response.status}: ${response.statusText}` }; + } + + console.error('[Search] Search failed:', { + status: response.status, + statusText: response.statusText, + error: errorData.error || errorData, + fullError: errorData + }); + + error = errorData.error || 'Search failed'; results = null; // Clear results on error } } catch (err) { // Ignore abort errors if (err instanceof Error && err.name === 'AbortError') { + console.log('[Search] Request was aborted'); return; } + + console.error('[Search] Search error:', { + error: err, + message: err instanceof Error ? err.message : String(err), + stack: err instanceof Error ? err.stack : undefined, + query: searchQuery + }); + error = err instanceof Error ? err.message : 'Search failed'; results = null; // Clear results on error } finally { // Only update loading state if this is still the current search if (currentAbortController === searchAbortController) { loading = false; + console.log('[Search] Loading state set to false'); } } }