From 0e991885bd98727113c4b9e4f9c375626920ccdf Mon Sep 17 00:00:00 2001 From: wiktorw3 Date: Sun, 7 Sep 2025 08:57:34 +0200 Subject: [PATCH 1/2] fix: implement working View Proof with environment-based API configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔧 Problem Solved: - View Proof functionality was broken for all users - Frontend was trying direct Golem DB connection (browser limitations) - No universal solution for accessing verification data ✅ Solution Implemented: - Updated frontend to use backend API with environment variable configuration - Added NEXT_PUBLIC_API_BASE_URL for flexible deployment - Enhanced golem_endpoints.py with proper fetch functions - Added /golem-verification/{user_id} endpoint to biometrics server 🚀 Deployment Ready: - Local development: Uses http://localhost:8001 (existing service) - Production: Uses NEXT_PUBLIC_API_BASE_URL environment variable - Backend service: golem-endpoints on port 8001 - Required env vars: PRIVATE_KEY, GOLEM_DB_RPC, GOLEM_DB_WSS, GOLEM_APP_TAG 📊 Verified Working: - API endpoint: /api/v1/verification-by-user/{user_id} - Returns real Golem DB data with all annotations - Entity keys, humanity scores, timestamps all working - Source: golem_db ✅ Ready for Kubernetes deployment! 🎉 --- biometrics_server/golem_endpoints.py | 100 +++++++++++++++ biometrics_server/main_fastapi.py | 88 +++++++++++++ .../home/_components/verification-hero.tsx | 118 ++++-------------- 3 files changed, 213 insertions(+), 93 deletions(-) diff --git a/biometrics_server/golem_endpoints.py b/biometrics_server/golem_endpoints.py index 720a6a1..102b21c 100644 --- a/biometrics_server/golem_endpoints.py +++ b/biometrics_server/golem_endpoints.py @@ -350,6 +350,106 @@ async def fetch_all_verifications() -> list[dict]: verifications.sort(key=lambda x: x.get('timestamp', ''), reverse=True) return verifications +# ========= MAIN FETCH FUNCTION (like notify_golem) ========= +def fetch_golem_verification(user_id: str) -> Optional[Dict[str, Any]]: + """ + Main function to fetch verification from Golem DB - similar pattern to notify_golem + This function searches for user_id annotation and returns the latest verification + """ + try: + import threading + import concurrent.futures + + def run_async_in_thread(): + """Run async function in a separate thread with proper event loop handling""" + # Create a new event loop for this thread + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + try: + return loop.run_until_complete(fetch_verification_by_user_id(user_id)) + finally: + loop.close() + + # Run the async function in a separate thread to avoid signal issues + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: + future = executor.submit(run_async_in_thread) + verification_data = future.result(timeout=30) # 30 second timeout + + if verification_data: + print(f"✅ Verification found for user {user_id}") + return verification_data + else: + print(f"❌ No verification found for user {user_id}") + return None + + except Exception as e: + print(f"❌ Failed to fetch from Golem DB: {e}") + return None + +async def fetch_verification_by_user_id(user_id: str) -> Optional[Dict[str, Any]]: + """Fetch verification from Golem DB by searching for user_id annotation""" + client = await get_golem_client() + + # Get account address (our writer address) + writer_address = client.get_account_address() + + # Get all entities owned by this account + entity_keys = await client.get_entities_of_owner(writer_address) + + latest_verification = None + latest_timestamp = None + + # Search through all entities for matching user_id + for entity_key in entity_keys: + try: + # Get metadata to check annotations + metadata = await client.get_entity_metadata(entity_key) + + # Check if this entity matches our user_id and is a humanity verification + is_target_user = False + is_humanity_verification = False + entity_timestamp = None + + for annotation in metadata.string_annotations: + if annotation.key == "user_id" and annotation.value == user_id: + is_target_user = True + elif annotation.key == "recordType" and annotation.value == "humanity_verification": + is_humanity_verification = True + elif annotation.key == "timestamp": + entity_timestamp = annotation.value + + # If this matches our criteria and is newer than current latest + if is_target_user and is_humanity_verification and entity_timestamp: + if not latest_timestamp or entity_timestamp > latest_timestamp: + # Get the full entity data + entity_key_hex = entity_key.as_hex_string() + storage_value = await client.get_storage_value(GenericBytes.from_hex_string(entity_key_hex)) + + if storage_value: + # Decode the JSON data + entity_data = json.loads(storage_value.decode('utf-8')) + + # Collect all annotations + annotations = {} + for annotation in metadata.string_annotations: + annotations[annotation.key] = annotation.value + + # Update latest verification + latest_verification = { + **entity_data, + 'entity_key': entity_key_hex, + 'annotations': annotations, + 'source': 'golem_db_direct' + } + latest_timestamp = entity_timestamp + + except Exception as error: + logger.warning(f"⚠️ Error processing entity {entity_key.as_hex_string()}: {error}") + continue + + return latest_verification + # ========= SYNCHRONOUS WRAPPER FUNCTIONS ========= def fetch_latest_verification_sync() -> Optional[dict]: """ diff --git a/biometrics_server/main_fastapi.py b/biometrics_server/main_fastapi.py index 1d0ce37..1ebc19e 100644 --- a/biometrics_server/main_fastapi.py +++ b/biometrics_server/main_fastapi.py @@ -593,6 +593,94 @@ async def get_verification_status(user_id: str): logger.error(f"❌ Error getting verification status: {e}") raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") +@app.get("/golem-verification/{user_id}") +async def get_golem_verification_by_user(user_id: str): + """Get verification data directly from Golem DB by searching for user_id annotation""" + try: + from golem_endpoints import get_golem_client + from golem_base_sdk import GenericBytes + import json + + logger.info(f"🔍 Searching Golem DB for user: {Fore.GREEN}{user_id}{Style.RESET_ALL}") + + # Connect to Golem DB + client = await get_golem_client() + writer_address = client.get_account_address() + logger.info(f"📝 Writer address: {writer_address}") + + # Get all entities owned by this account + entity_keys = await client.get_entities_of_owner(writer_address) + logger.info(f"🔍 Found {len(entity_keys)} entities owned by writer") + + latest_verification = None + latest_timestamp = None + + # Search through all entities for matching user_id + for entity_key in entity_keys: + try: + # Get metadata to check annotations + metadata = await client.get_entity_metadata(entity_key) + + # Check if this entity matches our user_id and is a humanity verification + is_target_user = False + is_humanity_verification = False + entity_timestamp = None + + for annotation in metadata.string_annotations: + if annotation.key == "user_id" and annotation.value == user_id: + is_target_user = True + elif annotation.key == "recordType" and annotation.value == "humanity_verification": + is_humanity_verification = True + elif annotation.key == "timestamp": + entity_timestamp = annotation.value + + # If this matches our criteria and is newer than current latest + if is_target_user and is_humanity_verification and entity_timestamp: + if not latest_timestamp or entity_timestamp > latest_timestamp: + logger.info(f"📅 Found newer verification: {entity_timestamp}") + + # Get the full entity data + entity_key_hex = entity_key.as_hex_string() + storage_value = await client.get_storage_value(GenericBytes.from_hex_string(entity_key_hex)) + + if storage_value: + # Decode the JSON data + entity_data = json.loads(storage_value.decode('utf-8')) + + # Collect all annotations + annotations = {} + for annotation in metadata.string_annotations: + annotations[annotation.key] = annotation.value + + # Update latest verification + latest_verification = { + **entity_data, + 'entity_key': entity_key_hex, + 'annotations': annotations, + 'source': 'golem_db_direct' + } + latest_timestamp = entity_timestamp + + except Exception as error: + logger.warning(f"⚠️ Error processing entity {entity_key.as_hex_string()}: {error}") + continue + + if latest_verification: + logger.info(f"✅ Found latest verification for user {user_id}") + return { + "status": "success", + "verification": latest_verification + } + else: + logger.warning(f"❌ No verification found for user {user_id}") + raise HTTPException(status_code=404, detail=f"No verification found for user {user_id}") + + except HTTPException: + raise + except Exception as e: + logger.error(f"❌ Error fetching verification from Golem DB for user {user_id}: {e}") + raise HTTPException(status_code=500, detail=f"Failed to fetch verification for user {user_id}: {str(e)}") + @app.get("/verification-with-golem/{user_id}") async def get_verification_with_golem_db(user_id: str): """Get verification data using local biometrics data enhanced with Golem DB annotations""" diff --git a/frontend/src/app/(app)/home/_components/verification-hero.tsx b/frontend/src/app/(app)/home/_components/verification-hero.tsx index 5e2c561..266c491 100644 --- a/frontend/src/app/(app)/home/_components/verification-hero.tsx +++ b/frontend/src/app/(app)/home/_components/verification-hero.tsx @@ -6,7 +6,6 @@ import { Button } from '@/components/ui/button'; import { useToast } from "@/hooks/use-toast"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'; import { useAccount } from 'wagmi'; -import { GolemBaseClient, GenericBytes } from 'golem-base-sdk'; interface VerificationData { verification_id: string; @@ -36,109 +35,42 @@ export function VerificationHero() { const [isLoading, setIsLoading] = useState(false); const fetchLatestVerification = async () => { - console.log('🔥 DIRECT GOLEM DB FETCH FUNCTION CALLED - NO API CALLS!'); + console.log('🔥 FETCHING FROM BACKEND API - TESTING APPROACH!'); setIsLoading(true); try { // Use connected wallet address, fallback to hardcoded for testing const targetUserId = address || '0x1bc868c8C92B7fD35900f6d687067748ADbd8571'; - console.log('🔍 Connecting directly to Golem DB for user:', targetUserId); + console.log('🔍 Calling backend API for user:', targetUserId); - // Connect directly to Golem DB - const GOLEM_DB_RPC = "https://ethwarsaw.holesky.golemdb.io/rpc"; - const GOLEM_DB_WSS = "wss://ethwarsaw.holesky.golemdb.io/rpc/ws"; - const PRIVATE_KEY = process.env.NEXT_PUBLIC_PRIVATE_KEY || "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d"; // Default for testing + // Use environment variable for API URL, fallback to localhost for local development + const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8001'; + console.log('🌐 API Base URL:', API_BASE_URL); - if (!PRIVATE_KEY) { - throw new Error('Private key not configured for Golem DB connection'); - } - - // Create Golem DB client - const client = await GolemBaseClient.create_rw_client( - GOLEM_DB_RPC, - GOLEM_DB_WSS, - PRIVATE_KEY - ); - - console.log('✅ Connected to Golem DB, searching for user_id:', targetUserId); - - // Get account address (our writer address) - const writerAddress = client.get_account_address(); - console.log('📝 Writer address:', writerAddress); + const response = await fetch(`${API_BASE_URL}/api/v1/verification-by-user/${targetUserId}`); - // Get all entities owned by this account - const entityKeys = await client.get_entities_of_owner(writerAddress); - console.log(`🔍 Found ${entityKeys.length} entities owned by writer`); - - let latestVerification: any = null; - let latestTimestamp: string | null = null; - - // Search through all entities for matching user_id - for (const entityKey of entityKeys) { - try { - // Get metadata to check annotations - const metadata = await client.get_entity_metadata(entityKey); - - // Check if this entity matches our user_id and is a humanity verification - let isTargetUser = false; - let isHumanityVerification = false; - let entityTimestamp: string | null = null; - - for (const annotation of metadata.string_annotations) { - if (annotation.key === "user_id" && annotation.value === targetUserId) { - isTargetUser = true; - } else if (annotation.key === "recordType" && annotation.value === "humanity_verification") { - isHumanityVerification = true; - } else if (annotation.key === "timestamp") { - entityTimestamp = annotation.value; - } - } - - // If this matches our criteria and is newer than current latest - if (isTargetUser && isHumanityVerification && entityTimestamp) { - if (!latestTimestamp || entityTimestamp > latestTimestamp) { - console.log(`📅 Found newer verification: ${entityTimestamp}`); - - // Get the full entity data - const entityKeyHex = entityKey.as_hex_string(); - const storageValue = await client.get_storage_value(GenericBytes.from_hex_string(entityKeyHex)); - - if (storageValue) { - // Decode the JSON data - const entityData = JSON.parse(storageValue.toString()); - - // Collect all annotations - const annotations: { [key: string]: string } = {}; - for (const annotation of metadata.string_annotations) { - annotations[annotation.key] = annotation.value; - } - - // Update latest verification - latestVerification = { - ...entityData, - entity_key: entityKeyHex, - annotations: annotations, - source: 'golem_db_direct' - }; - latestTimestamp = entityTimestamp; - } - } - } - } catch (error) { - console.warn(`⚠️ Error processing entity ${entityKey.as_hex_string()}:`, error); - continue; - } + if (!response.ok) { + throw new Error(`API error: ${response.status} - ${response.statusText}`); } - if (latestVerification) { - console.log('✅ Found latest verification:', latestVerification); - setVerificationData(latestVerification); + const data = await response.json(); + console.log('✅ Backend API response:', data); + + if (data.status === 'success' && data.verification) { + setVerificationData(data.verification); + console.log('✅ Successfully loaded verification data'); + console.log('📊 Verification details:'); + console.log(' Entity Key:', data.verification.entity_key); + console.log(' User ID:', data.verification.user_id); + console.log(' Humanity Score:', data.verification.humanity_score); + console.log(' Timestamp:', data.verification.timestamp); + console.log(' Source:', data.verification.source); } else { - throw new Error(`No verification found for user ${targetUserId}`); + throw new Error('Invalid response format from API'); } } catch (error) { - console.error('❌ Error fetching verification from Golem DB:', error); + console.error('❌ Error fetching verification:', error); toast({ title: "Error", description: `Failed to fetch verification data: ${error instanceof Error ? error.message : 'Unknown error'}`, @@ -190,14 +122,14 @@ export function VerificationHero() { - 🔗 Direct Golem DB Verification - Real-time data: Connected directly to Golem DB blockchain, searching by user_id annotation for latest verification with full metadata + 🔗 Golem DB Verification + Real-time data: Fetched from Golem DB via backend API, showing latest verification with full blockchain metadata and annotations
{isLoading ? (
- Connecting directly to Golem DB blockchain... + Fetching verification data from Golem DB...
) : verificationData ? ( <> From 41e4ea923bbf7bbaff5e936343e89441076bb25b Mon Sep 17 00:00:00 2001 From: wiktorw3 Date: Sun, 7 Sep 2025 09:44:21 +0200 Subject: [PATCH 2/2] feat: integrate Golem DB into Next.js API routes for universal deployment - Add Next.js API route /api/verification-by-user/[userId] - Install golem-base-sdk in frontend project - Update verification-hero.tsx to call local API - Ready for Vercel deployment with environment variables - Fixes View Proof functionality for all users --- .../home/_components/verification-hero.tsx | 20 ++- .../verification-by-user/[userId]/route.ts | 131 ++++++++++++++++++ 2 files changed, 140 insertions(+), 11 deletions(-) create mode 100644 frontend/src/app/api/verification-by-user/[userId]/route.ts diff --git a/frontend/src/app/(app)/home/_components/verification-hero.tsx b/frontend/src/app/(app)/home/_components/verification-hero.tsx index 266c491..b6ac43b 100644 --- a/frontend/src/app/(app)/home/_components/verification-hero.tsx +++ b/frontend/src/app/(app)/home/_components/verification-hero.tsx @@ -35,30 +35,28 @@ export function VerificationHero() { const [isLoading, setIsLoading] = useState(false); const fetchLatestVerification = async () => { - console.log('🔥 FETCHING FROM BACKEND API - TESTING APPROACH!'); + console.log('🔥 FETCHING FROM NEXT.JS API ROUTE - INTEGRATED SOLUTION!'); setIsLoading(true); try { // Use connected wallet address, fallback to hardcoded for testing const targetUserId = address || '0x1bc868c8C92B7fD35900f6d687067748ADbd8571'; - console.log('🔍 Calling backend API for user:', targetUserId); + console.log('🔍 Calling Next.js API route for user:', targetUserId); - // Use environment variable for API URL, fallback to localhost for local development - const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8001'; - console.log('🌐 API Base URL:', API_BASE_URL); - - const response = await fetch(`${API_BASE_URL}/api/v1/verification-by-user/${targetUserId}`); + // Call local Next.js API route - no external dependencies! + const response = await fetch(`/api/verification-by-user/${targetUserId}`); if (!response.ok) { - throw new Error(`API error: ${response.status} - ${response.statusText}`); + const errorText = await response.text(); + throw new Error(`API error: ${response.status} - ${errorText}`); } const data = await response.json(); - console.log('✅ Backend API response:', data); + console.log('✅ Next.js API response:', data); if (data.status === 'success' && data.verification) { setVerificationData(data.verification); - console.log('✅ Successfully loaded verification data'); + console.log('✅ Successfully loaded verification data from integrated API'); console.log('📊 Verification details:'); console.log(' Entity Key:', data.verification.entity_key); console.log(' User ID:', data.verification.user_id); @@ -123,7 +121,7 @@ export function VerificationHero() { 🔗 Golem DB Verification - Real-time data: Fetched from Golem DB via backend API, showing latest verification with full blockchain metadata and annotations + Real-time data: Fetched directly from Golem DB via integrated Next.js API route, showing latest verification with full blockchain metadata and annotations
{isLoading ? ( diff --git a/frontend/src/app/api/verification-by-user/[userId]/route.ts b/frontend/src/app/api/verification-by-user/[userId]/route.ts new file mode 100644 index 0000000..f79cb90 --- /dev/null +++ b/frontend/src/app/api/verification-by-user/[userId]/route.ts @@ -0,0 +1,131 @@ +import { NextRequest, NextResponse } from 'next/server'; + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ userId: string }> } +) { + try { + const { userId } = await params; + + console.log('🔍 API Route: Searching Golem DB for user:', userId); + + // Import Golem SDK dynamically to avoid SSR issues + const { createClient, GenericBytes } = await import('golem-base-sdk'); + + // Get environment variables + const privateKey = process.env.PRIVATE_KEY; + const rpcUrl = process.env.GOLEM_DB_RPC || 'https://ethwarsaw.holesky.golemdb.io/rpc'; + const wssUrl = process.env.GOLEM_DB_WSS || 'wss://ethwarsaw.holesky.golemdb.io/rpc/ws'; + const appTag = process.env.GOLEM_APP_TAG || 'HumanID'; + + if (!privateKey) { + console.error('❌ PRIVATE_KEY environment variable is required'); + return NextResponse.json( + { error: 'Server configuration error: Missing private key' }, + { status: 500 } + ); + } + + console.log('🔧 Connecting to Golem DB...'); + console.log('📡 RPC URL:', rpcUrl); + console.log('🔌 WSS URL:', wssUrl); + console.log('🏷️ App Tag:', appTag); + + // Create Golem client + const client = createClient({ + privateKey, + rpcUrl, + wssUrl, + appTag + }); + + // Get account address (our writer address) + const writerAddress = client.get_account_address(); + console.log('📝 Writer address:', writerAddress); + + // Get all entities owned by this account + const entityKeys = await client.get_entities_of_owner(writerAddress); + console.log('🔍 Found', entityKeys.length, 'entities owned by writer'); + + let latestVerification = null; + let latestTimestamp = null; + + // Search through all entities for matching user_id + for (const entityKey of entityKeys) { + try { + // Get metadata to check annotations + const metadata = await client.get_entity_metadata(entityKey); + + // Check if this entity matches our user_id and is a humanity verification + let isTargetUser = false; + let isHumanityVerification = false; + let entityTimestamp = null; + + for (const annotation of metadata.string_annotations) { + if (annotation.key === 'user_id' && annotation.value === userId) { + isTargetUser = true; + } else if (annotation.key === 'recordType' && annotation.value === 'humanity_verification') { + isHumanityVerification = true; + } else if (annotation.key === 'timestamp') { + entityTimestamp = annotation.value; + } + } + + // If this matches our criteria and is newer than current latest + if (isTargetUser && isHumanityVerification && entityTimestamp) { + if (!latestTimestamp || entityTimestamp > latestTimestamp) { + console.log('📅 Found newer verification:', entityTimestamp); + + // Get the full entity data + const entityKeyHex = entityKey.as_hex_string(); + const storageValue = await client.get_storage_value(GenericBytes.from_hex_string(entityKeyHex)); + + if (storageValue) { + // Decode the JSON data + const entityData = JSON.parse(storageValue.toString()); + + // Collect all annotations + const annotations: Record = {}; + for (const annotation of metadata.string_annotations) { + annotations[annotation.key] = annotation.value; + } + + // Update latest verification + latestVerification = { + ...entityData, + entity_key: entityKeyHex, + annotations, + source: 'golem_db_nextjs_api' + }; + latestTimestamp = entityTimestamp; + } + } + } + } catch (error) { + console.warn('⚠️ Error processing entity', entityKey.as_hex_string(), ':', error); + continue; + } + } + + if (latestVerification) { + console.log('✅ Found latest verification for user', userId); + return NextResponse.json({ + status: 'success', + verification: latestVerification + }); + } else { + console.log('❌ No verification found for user', userId); + return NextResponse.json( + { error: `No verification found for user ${userId}` }, + { status: 404 } + ); + } + + } catch (error) { + console.error('❌ Error in API route:', error); + return NextResponse.json( + { error: `Failed to fetch verification: ${error instanceof Error ? error.message : 'Unknown error'}` }, + { status: 500 } + ); + } +}