This commit is contained in:
zramsay 2025-03-21 21:11:00 -04:00
parent c44ba31b04
commit 89484ad2a9
3 changed files with 67 additions and 211 deletions

View File

@ -146,34 +146,11 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
const formData = await req.formData()
const imageFile = formData.get('image')
// Check for wallet address in form data, URL params, and headers
let walletAddress = formData.get('walletAddress');
// Check URL query parameters (most reliable on Vercel)
const url = new URL(req.url);
const queryWalletAddress = url.searchParams.get('wallet');
if (queryWalletAddress && !walletAddress) {
console.log('Found wallet address in URL query parameter:', queryWalletAddress);
walletAddress = queryWalletAddress;
}
// Also check custom header (less reliable with Vercel)
const walletHeaderAddress = req.headers.get('X-Wallet-Address');
if (walletHeaderAddress && !walletAddress) {
console.log('Found wallet address in custom header:', walletHeaderAddress);
walletAddress = walletHeaderAddress;
}
// Log more detailed data about the wallet address
// Simple request data logging
console.log('Request data received:', {
hasImage: !!imageFile,
imageType: imageFile ? typeof imageFile : 'undefined',
hasWalletAddressInForm: !!formData.get('walletAddress'),
hasWalletAddressInHeader: !!walletHeaderAddress,
hasWalletAddressInQuery: !!queryWalletAddress,
requestUrl: req.url,
searchParams: Object.fromEntries(url.searchParams.entries()),
finalWalletAddress: walletAddress || 'Not provided',
formDataKeys: Array.from(formData.keys())
});
@ -249,11 +226,12 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
: "🌿 No wildlife detected in this image. Try uploading a photo of an animal!"
}`
// Prepare user response with extra fields for token data we'll add later
// Prepare user response with animal type for token awarding later
const responseData = {
description: responseMessage,
isAnimal: visionResult.isAnimal,
tokenReward: null
animalType: visionResult.mainObject || 'Unknown wildlife',
pointsAwarded: visionResult.isAnimal ? 50 : 0 // Default points value
};
// Background processing for animal images
@ -393,76 +371,8 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
// Even if points award fails, we continue to try token rewards
}
// AFTER points are awarded, try to award tokens if wallet is connected
// This is separate so issues with token rewards don't affect points
try {
// We already extracted the wallet address at the beginning of the request
// walletAddress is already defined in the outer scope
// Ensure it's a string type
const walletAddressString = walletAddress ? String(walletAddress) : null;
console.log('Wallet address for token award (final check):', {
walletAddress,
walletAddressString,
walletAddressType: typeof walletAddress
});
// Log the wallet address for debugging
console.log('Wallet address for token award:', {
processedValue: walletAddress || 'Not provided',
valueType: typeof walletAddress
});
// Import dynamically to avoid server-side issues with window object
const { awardTokensForSighting } = await import('../../../services/blockchain/tokenRewardService');
// Get species from vision result for token amount calculation
const species = visionResult.mainObject || 'animal';
// Skip token award if no wallet address is provided
if (!walletAddress) {
console.log('No wallet address provided, skipping token award');
// Continue with other operations without token reward
} else {
// Log wallet information
console.log('Token award attempt:', {
walletAddressProvided: true,
walletAddress: walletAddress,
species
});
// Log detailed information for debugging
console.log('Starting token award with explicit address:', walletAddress);
// Attempt to award tokens for this wildlife sighting
// Make sure we're passing a properly processed string
const tokenResult = await awardTokensForSighting(
species,
undefined,
walletAddressString // Use the explicitly string-converted version
);
// Add token reward info to response if successful
if (tokenResult.success) {
responseData.tokenReward = {
amount: tokenResult.tokenAmount,
txHash: tokenResult.txHash
};
console.log('Token reward successful:', tokenResult);
} else if (tokenResult.walletConnected) {
// Wallet was connected but award failed
console.error('Token reward failed:', tokenResult.error);
} else {
// No wallet connected - this is not an error, just informational
console.log('No wallet connected, skipping token reward');
}
}
} catch (tokenError) {
console.error('Error processing token reward:', tokenError);
// Continue without token rewards if there's an error
}
// Note: We've moved token awarding to a separate endpoint
// This simplifies the flow and avoids wallet connection issues on the server
}
} catch (err) {
console.error('Failed to award points for image:', err);

View File

@ -54,9 +54,6 @@ const WildlifeIdentifier: React.FC = () => {
return;
}
// No need to check for wallet connection - the process works for both user types
// Tokens will be awarded automatically if wallet is connected
setIsProcessing(true);
setError(null);
@ -65,7 +62,6 @@ const WildlifeIdentifier: React.FC = () => {
const formData = new FormData();
formData.append('image', selectedImage);
// Add wallet address to the request if connected
// Get the wallet address both from memory and localStorage as backup
let userAddress = getWalletAddress();
@ -82,45 +78,12 @@ const WildlifeIdentifier: React.FC = () => {
}
}
// Hard-code a wallet connection check - let's be very explicit
const isConnected = isWalletConnected();
console.log('Explicitly checking wallet connection status:', isConnected);
if (userAddress) {
console.log('Found wallet address to include in upload:', userAddress);
// Explicitly add as string to avoid any type issues
formData.append('walletAddress', String(userAddress));
// Debug check - verify the value was properly added
const check = formData.get('walletAddress');
console.log('Verification after append:', {
walletAddressInForm: check,
matches: check === userAddress,
formDataKeys: Array.from(formData.keys())
});
} else {
console.log('No wallet address found for upload');
// Force add a wallet check flag to help debug
formData.append('walletChecked', 'true');
formData.append('walletStatus', isConnected ? 'connected_but_no_address' : 'not_connected');
}
console.log('Uploading image for analysis...');
console.log('Wallet connection status:', isWalletConnected());
console.log('Wallet connection status:', isConnected);
console.log('User wallet address:', userAddress || 'Not connected');
// Construct the URL with wallet address as a query parameter if available
let url = '/api/analyze';
if (userAddress) {
// Encode the wallet address for URL safety
const encodedWalletAddress = encodeURIComponent(userAddress);
url += `?wallet=${encodedWalletAddress}`;
console.log('Adding wallet address as query parameter:', userAddress);
}
// Send to the Next.js API route with wallet in the URL
const response = await fetch(url, {
// Send to the Next.js API route for analysis (points only)
const response = await fetch('/api/analyze', {
method: 'POST',
body: formData
});
@ -133,12 +96,43 @@ const WildlifeIdentifier: React.FC = () => {
const analysisResult = await response.json();
console.log('Analysis result:', analysisResult);
// IMPORTANT: New approach - if the analysis was successful and we have a wallet address,
// make a separate call to award tokens
let tokenResult = null;
if (analysisResult.isAnimal && userAddress && isConnected) {
try {
console.log('Animal detected and wallet connected. Awarding tokens separately...');
const tokenResponse = await fetch('/api/award-tokens', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
walletAddress: userAddress,
animalName: analysisResult.animalType || 'Unknown wildlife'
})
});
if (tokenResponse.ok) {
tokenResult = await tokenResponse.json();
console.log('Token award result:', tokenResult);
} else {
console.error('Failed to award tokens:', await tokenResponse.json());
}
} catch (tokenError) {
console.error('Error awarding tokens:', tokenError);
}
}
// Format the result for display
setResult({
success: analysisResult.isAnimal,
message: analysisResult.description,
rewardAmount: analysisResult.tokenReward?.amount?.toString(),
txHash: analysisResult.tokenReward?.txHash,
// If we have token results from the separate endpoint, use those
rewardAmount: tokenResult?.amount?.toString() || analysisResult.pointsAwarded?.toString() || '0',
txHash: tokenResult?.txHash,
});
} catch (err) {
setError(err instanceof Error ? err.message : 'Error processing image');

View File

@ -36,94 +36,45 @@ export const isRareSpecies = (species: string): boolean => {
};
/**
* Award tokens for a wildlife sighting if wallet is connected
* Award tokens for a wildlife sighting with an explicit wallet address
* @param walletAddress The recipient wallet address
* @param userEmail The user's email (for logging/tracking)
* @param species The detected animal species
* @param points Optional number of points to convert to tokens (if not provided, calculates based on species)
* @param explicitWalletAddress Optional wallet address to use instead of checking connection state
* @returns Object containing success status and transaction info
*/
export const awardTokensForSighting = async (
species: string,
points?: number,
explicitWalletAddress?: string
walletAddress: string,
userEmail: string,
species: string
): Promise<{
success: boolean;
tokenAmount?: number;
amount?: number;
txHash?: string;
error?: string;
walletConnected: boolean;
}> => {
try {
// Determine wallet address - either from parameter or connected wallet
let walletAddress = explicitWalletAddress;
// If explicit address was provided, use it directly (server-side case)
if (walletAddress) {
console.log('Using explicitly provided wallet address:', walletAddress);
} else {
// Client-side case - check if wallet is connected
console.log('No explicit wallet address provided, checking wallet connection state...');
// Try to get from memory first
if (isWalletConnected()) {
walletAddress = getWalletAddress();
console.log('Found connected wallet in memory:', walletAddress);
}
// Try localStorage as a fallback (client-side only)
if (!walletAddress && typeof window !== 'undefined') {
try {
const storedAddress = localStorage.getItem('wildlife_wallet_address');
if (storedAddress) {
console.log('Using wallet address from localStorage:', storedAddress);
walletAddress = storedAddress;
}
} catch (e) {
console.warn('Error accessing localStorage:', e);
}
}
// If we still don't have an address, return no wallet connected
if (!walletAddress) {
console.log('No wallet connected, skipping token award.');
return {
success: false,
error: 'No wallet connected',
walletConnected: false
};
}
}
// Validate we have a wallet address by this point
if (!walletAddress) {
console.log('No valid wallet address available for token award.');
console.error('No wallet address provided for token award');
return {
success: false,
error: 'No wallet address available',
walletConnected: false
error: 'No wallet address provided'
};
}
console.log('Processing token award for wallet:', walletAddress);
console.log(`Processing token award for wallet ${walletAddress} and species ${species}`);
// Calculate token reward amount - either from points or from species
let totalReward: number;
if (typeof points === 'number' && points > 0) {
// If points are provided, use them directly (1 point = 1 token)
totalReward = points;
} else {
// Otherwise calculate based on species
const baseReward = TOKEN_REWARDS.WILDLIFE_SIGHTING;
const isRare = isRareSpecies(species);
const rareBonus = isRare ? TOKEN_REWARDS.RARE_SPECIES : 0;
totalReward = baseReward + rareBonus;
}
// Calculate token reward amount based on species
const baseReward = TOKEN_REWARDS.WILDLIFE_SIGHTING;
const isRare = isRareSpecies(species);
const rareBonus = isRare ? TOKEN_REWARDS.RARE_SPECIES : 0;
const totalReward = baseReward + rareBonus;
// Call the backend service to award tokens through the distributor
const apiUrl = process.env.NEXT_PUBLIC_TOKEN_BACKEND_URL || 'http://localhost:3001';
const apiKey = process.env.NEXT_PUBLIC_TOKEN_API_KEY || '';
console.log(`Sending token award request to ${apiUrl}/api/award-tokens`);
// Request tokens from the backend service
const response = await fetch(`${apiUrl}/api/award-tokens`, {
method: 'POST',
@ -133,24 +84,26 @@ export const awardTokensForSighting = async (
},
body: JSON.stringify({
recipientAddress: walletAddress,
species: species.toLowerCase()
species: species.toLowerCase(),
userEmail
})
});
if (!response.ok) {
const errorData = await response.json();
console.error('Token service error response:', errorData);
throw new Error(errorData.error || 'Error from token service');
}
const result = await response.json();
console.log('Token service success response:', result);
if (result.success) {
console.log(`Awarded tokens for ${species} sighting to wallet ${walletAddress}`);
console.log(`Awarded ${result.amount || totalReward} tokens for ${species} to wallet ${walletAddress}`);
return {
success: true,
tokenAmount: result.amount,
txHash: result.txHash,
walletConnected: true
amount: result.amount || totalReward,
txHash: result.txHash
};
} else {
throw new Error(result.error || 'Failed to award tokens');
@ -159,8 +112,7 @@ export const awardTokensForSighting = async (
console.error('Error in token reward service:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error processing token reward',
walletConnected: true
error: error instanceof Error ? error.message : 'Unknown error processing token reward'
};
}
};