From 3f6052e5edb76c5b01b60fa994bf4f5411208810 Mon Sep 17 00:00:00 2001 From: zramsay Date: Fri, 21 Mar 2025 21:24:50 -0400 Subject: [PATCH] not earn --- src/app/api/award-tokens/route.ts | 71 +++++++++++++++++++++++++++ src/components/ImageAnalysisCard.tsx | 55 ++++++++++++++++++++- src/components/WildlifeIdentifier.tsx | 42 +++++++++++++--- src/services/googleVisionService.ts | 2 + 4 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 src/app/api/award-tokens/route.ts diff --git a/src/app/api/award-tokens/route.ts b/src/app/api/award-tokens/route.ts new file mode 100644 index 0000000..719b57b --- /dev/null +++ b/src/app/api/award-tokens/route.ts @@ -0,0 +1,71 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/lib/auth'; +import { awardTokensForSighting } from '@/services/blockchain/tokenRewardService'; +import { logger } from '@/services/constants'; + +export async function POST(req: NextRequest) { + try { + logger.info('award-tokens API called'); + + // Log all request headers for debugging + logger.info('Request headers:', Object.fromEntries(req.headers.entries())); + + // Make authentication optional for demonstration - we'll allow both authenticated and + // unauthenticated requests for testing purposes + let userEmail = 'unknown@example.com'; + + try { + const session = await getServerSession(authOptions); + if (session?.user?.email) { + userEmail = session.user.email; + logger.info(`Authenticated user: ${userEmail}`); + } else { + logger.warn('No authenticated session found, but continuing for demonstration'); + } + } catch (authError) { + logger.error('Authentication error but continuing:', authError); + } + + // Parse request body + let data; + try { + data = await req.json(); + logger.info('Request body:', data); + } catch (parseError) { + logger.error('Failed to parse request body:', parseError); + return NextResponse.json({ error: 'Invalid request body' }, { status: 400 }); + } + + const { walletAddress, animalName } = data; + + if (!walletAddress) { + logger.error('No wallet address provided for token award'); + return NextResponse.json({ error: 'Wallet address is required' }, { status: 400 }); + } + + logger.info(`Processing token award for wallet ${walletAddress} and animal ${animalName || 'Unknown'}`); + + // Check for environment variables + const apiUrl = process.env.NEXT_PUBLIC_TOKEN_BACKEND_URL; + const apiKey = process.env.NEXT_PUBLIC_TOKEN_API_KEY; + + logger.info(`Using backend URL: ${apiUrl || 'Not set'}, API key exists: ${!!apiKey}`); + + const result = await awardTokensForSighting(walletAddress, userEmail, animalName || 'Unknown wildlife'); + + logger.info(`Token award result:`, result); + + // Return the complete result directly without nesting + return NextResponse.json(result); + } catch (error) { + logger.error('Error in award-tokens API:', error); + return NextResponse.json({ + success: false, + error: error instanceof Error ? error.message : 'Failed to award tokens' + }, { status: 500 }); + } +} + +// Ensure dynamic routing +export const dynamic = 'force-dynamic' \ No newline at end of file diff --git a/src/components/ImageAnalysisCard.tsx b/src/components/ImageAnalysisCard.tsx index 80b1f30..7bc11cd 100644 --- a/src/components/ImageAnalysisCard.tsx +++ b/src/components/ImageAnalysisCard.tsx @@ -85,12 +85,65 @@ const ImageAnalysisCard: React.FC = ({ } if (result.description) { + // Check if we should award tokens - if an animal was identified and wallet is connected + let tokenReward = result.tokenReward || null; + + // If the image contains an animal and the user has a wallet connected + if (result.isAnimal && isWalletConnected()) { + try { + console.log('Animal detected and wallet connected. Attempting to award tokens...'); + + // Import the necessary services + const { getWalletAddress } = await import('../services/blockchain/seiService'); + const walletAddress = getWalletAddress(); + + if (walletAddress) { + console.log(`Found wallet address: ${walletAddress}, awarding tokens`); + + // Call our new token award endpoint + const tokenResponse = await fetch('/api/award-tokens', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + walletAddress: walletAddress, + animalName: result.animalType || 'Unknown wildlife' + }) + }); + + if (tokenResponse.ok) { + const tokenResult = await tokenResponse.json(); + console.log('Token award result:', tokenResult); + + if (tokenResult.success) { + // Update token reward information + tokenReward = { + amount: tokenResult.amount, + txHash: tokenResult.txHash + }; + console.log(`Successfully awarded ${tokenResult.amount} tokens`); + } else { + console.error('Token award failed:', tokenResult.error); + } + } else { + console.error('Failed to call token award endpoint:', tokenResponse.status); + } + } else { + console.log('Wallet is connected but no address available'); + } + } catch (tokenError) { + console.error('Error awarding tokens:', tokenError); + // Continue with analysis result even if token award fails + } + } + setAnalysisState({ loading: false, imageUrl: analysisState.imageUrl, description: result.description, error: null, - tokenReward: result.tokenReward || null + tokenReward: tokenReward }) } else { throw new Error('No analysis received') diff --git a/src/components/WildlifeIdentifier.tsx b/src/components/WildlifeIdentifier.tsx index c984a2b..32e3efb 100644 --- a/src/components/WildlifeIdentifier.tsx +++ b/src/components/WildlifeIdentifier.tsx @@ -100,30 +100,56 @@ const WildlifeIdentifier: React.FC = () => { // make a separate call to award tokens let tokenResult = null; + // ENHANCED DEBUG: Check each condition separately with explicit logging + console.log('Token award eligibility check:', { + isAnimal: analysisResult.isAnimal, + userAddressAvailable: !!userAddress, + walletIsConnected: isConnected, + userAddressValue: userAddress, + animalType: analysisResult.animalType + }); + if (analysisResult.isAnimal && userAddress && isConnected) { try { - console.log('Animal detected and wallet connected. Awarding tokens separately...'); + console.log('✅ All conditions met! Awarding tokens separately...'); + + // Log the exact request we're about to make + const requestBody = { + walletAddress: userAddress, + animalName: analysisResult.animalType || 'Unknown wildlife' + }; + console.log('Token award request body:', JSON.stringify(requestBody)); const tokenResponse = await fetch('/api/award-tokens', { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ - walletAddress: userAddress, - animalName: analysisResult.animalType || 'Unknown wildlife' - }) + body: JSON.stringify(requestBody) }); + console.log('Token award response status:', tokenResponse.status); + if (tokenResponse.ok) { tokenResult = await tokenResponse.json(); - console.log('Token award result:', tokenResult); + console.log('Token award success result:', tokenResult); + + // The response format is now different (no nested result structure) + // so we use the response directly + if (tokenResult.success) { + console.log(`Successfully awarded ${tokenResult.amount} tokens with tx hash ${tokenResult.txHash}`); + } else { + console.error('Token award returned failure status:', tokenResult.error); + } } else { - console.error('Failed to award tokens:', await tokenResponse.json()); + const errorData = await tokenResponse.json().catch(e => ({ error: 'Failed to parse error response' })); + console.error('Failed to award tokens:', errorData); } } catch (tokenError) { - console.error('Error awarding tokens:', tokenError); + console.error('Exception during token award process:', tokenError); } + } else { + console.log('❌ Not awarding tokens - conditions not met'); } // Format the result for display diff --git a/src/services/googleVisionService.ts b/src/services/googleVisionService.ts index dfa8aca..7ec4de3 100644 --- a/src/services/googleVisionService.ts +++ b/src/services/googleVisionService.ts @@ -7,6 +7,8 @@ export interface VisionAnalysisResult { error?: string isAnimal?: boolean rawResponse?: any + animalType?: string + pointsAwarded?: number } export interface VisionConfig {