diff --git a/web/app/api/validate/route.js b/web/app/api/validate/route.js
index b69b2d9..c1c38a7 100644
--- a/web/app/api/validate/route.js
+++ b/web/app/api/validate/route.js
@@ -1,119 +1,241 @@
import { NextResponse } from 'next/server';
-import { classifyImage } from '@/lib/cnnClient';
-import { isVisible } from '@/lib/visibilityCheck';
-import { isDuplicate } from '@/lib/imageHash';
-import { uploadMetadataToIPFS } from '@/lib/ipfs';
-import { mintObservation } from '@/lib/mintNFT';
-import { logValidation } from '@/lib/logValidation';
-import { writeFile } from 'fs/promises';
+// import { classifyImage } from '@/lib/cnnClient';
+// import { isVisible } from '@/lib/visibilityCheck';
+import { generateImageHash, compareHashes, checkImageHash } from '@/lib/imageHash';
+// import { uploadMetadataToIPFS } from '@/lib/ipfs';
+// import { mintObservation } from '@/lib/mintNFT';
+// import { logValidation } from '@/lib/logValidation';
+import { writeFile, mkdir } from 'fs/promises';
import path from 'path';
import { randomUUID } from 'crypto';
+import { existsSync } from 'fs';
export const dynamic = 'force-dynamic'; // disable edge runtime (needed for fs & formData)
export async function POST(req) {
- const formData = await req.formData();
-
- const imageFile = formData.get('image');
- const userConstellation = formData.get('constellation');
- const latitude = formData.get('latitude');
- const longitude = formData.get('longitude');
- const timestamp = formData.get('timestamp');
- const userAddress = formData.get('userAddress');
-
- const logEntry = {
- imageBase64: imageFile ? await imageFile.text() : null,
- imageHash: '', // Placeholder, will be set after image processing
- geolocation: {
- lat: parseFloat(latitude),
- lng: parseFloat(longitude),
- },
- time: new Date(timestamp).toISOString(),
- constellation: userConstellation,
- confidenceScore: 0.92, // Placeholder confidence score
- isValid: false,
- reason: '',
- ipfsMetadataUri: '',
- txnHash: '',
- };
-
- if (!imageFile || !userConstellation || !latitude || !longitude || !timestamp || !userAddress) {
- logEntry.reason = 'Missing required fields';
- await logValidation(logEntry);
- return NextResponse.json({ validated: false, reason: logEntry.reason }, { status: 400 });
- }
-
+ console.log('๐ API route called');
+
try {
- // Save image to temp file
+ console.log('1๏ธโฃ Getting form data...');
+ const formData = await req.formData();
+ console.log('๐ FormData received');
+
+ console.log('2๏ธโฃ Extracting image file...');
+ const imageFile = formData.get('image');
+ console.log('๐ท Image file:', imageFile ? `${imageFile.name} (${imageFile.size} bytes)` : 'No image');
+
+ if (!imageFile) {
+ console.log('โ No image file provided');
+ return NextResponse.json({
+ validated: false,
+ reason: 'No image file provided'
+ }, { status: 400 });
+ }
+
+ // Extract other parameters (for future features)
+ const userConstellation = formData.get('constellation');
+ const latitude = formData.get('latitude');
+ const longitude = formData.get('longitude');
+ const timestamp = formData.get('timestamp');
+ const userAddress = formData.get('userAddress');
+
+ // Log entry for validation tracking (if logValidation is available)
+ const logEntry = {
+ imageBase64: null, // Will be set if needed
+ imageHash: '', // Will be set after duplicate check
+ geolocation: latitude && longitude ? {
+ lat: parseFloat(latitude),
+ lng: parseFloat(longitude),
+ } : null,
+ time: timestamp ? new Date(timestamp).toISOString() : new Date().toISOString(),
+ constellation: userConstellation || null,
+ confidenceScore: 0, // Will be set by CNN if available
+ isValid: false,
+ reason: '',
+ ipfsMetadataUri: '',
+ txnHash: '',
+ };
+
+ console.log('3๏ธโฃ Processing image...');
const bytes = await imageFile.arrayBuffer();
const buffer = Buffer.from(bytes);
- const tempFilePath = path.join('/tmp', `${randomUUID()}.jpg`);
+
+ console.log('4๏ธโฃ Creating temp directory...');
+ // Create temp directory if it doesn't exist
+ const tempDir = process.env.TEMP || 'C:\\temp';
+ if (!existsSync(tempDir)) {
+ await mkdir(tempDir, { recursive: true });
+ }
+
+ console.log('5๏ธโฃ Writing file...');
+ const tempFilePath = path.join(tempDir, `${randomUUID()}.jpg`);
await writeFile(tempFilePath, buffer);
+ console.log('๐ Saved image to:', tempFilePath);
- // 1. CNN Classification
- const cnnPredictionData = await classifyImage(tempFilePath);
- const cnnPrediction = cnnPredictionData.constellation;
- const cnnConfidence = cnnPredictionData.confidenceScore;
- if (cnnPrediction !== userConstellation) {
- logEntry.reason = 'Constellation mismatch';
- await logValidation(logEntry);
- return NextResponse.json({ validated: false, reason: logEntry.reason }, { status: 400 });
+ // 1. CNN Classification (if available and constellation is provided)
+ if (userConstellation) {
+ try {
+ // Uncomment when CNN is ready
+ // const cnnPredictionData = await classifyImage(tempFilePath);
+ // const cnnPrediction = cnnPredictionData.constellation;
+ // const cnnConfidence = cnnPredictionData.confidenceScore;
+ // logEntry.confidenceScore = cnnConfidence;
+
+ // if (cnnPrediction !== userConstellation) {
+ // logEntry.reason = 'Constellation mismatch';
+ // console.log('โ CNN Classification failed: Constellation mismatch');
+ // // await logValidation(logEntry); // Uncomment when available
+ // return NextResponse.json({ validated: false, reason: logEntry.reason }, { status: 400 });
+ // }
+ // console.log("โ
CNN Classification passed:", { userConstellation, cnnPrediction, cnnConfidence });
+ console.log("โญ๏ธ CNN Classification skipped (not implemented yet)");
+ } catch (error) {
+ console.log("โ ๏ธ CNN Classification unavailable:", error.message);
+ }
}
- console.log("User Constellation:", userConstellation, "CNN Prediction:", cnnPrediction, "Confidence:", cnnConfidence);
-
- // 2. Visibility Check
- const visible = await isVisible({
- constellation: userConstellation,
- latitude,
- longitude,
- timestamp,
- });
-
- if (!visible) {
- logEntry.reason = 'Constellation not visible at that location/time';
- await logValidation(logEntry);
- return NextResponse.json({ validated: false, reason: logEntry.reason }, { status: 400 });
+
+ // 2. Visibility Check (if location and time are provided)
+ if (userConstellation && latitude && longitude && timestamp) {
+ try {
+ // Uncomment when visibility check is ready
+ // const visible = await isVisible({
+ // constellation: userConstellation,
+ // latitude,
+ // longitude,
+ // timestamp,
+ // });
+
+ // if (!visible) {
+ // logEntry.reason = 'Constellation not visible at that location/time';
+ // console.log('โ Visibility check failed');
+ // // await logValidation(logEntry); // Uncomment when available
+ // return NextResponse.json({ validated: false, reason: logEntry.reason }, { status: 400 });
+ // }
+ // console.log("โ
Visibility check passed");
+ console.log("โญ๏ธ Visibility check skipped (not implemented yet)");
+ } catch (error) {
+ console.log("โ ๏ธ Visibility check unavailable:", error.message);
+ }
}
- console.log("Visibility Check:", visible);
- // 3. Duplicate Check
- const isDup = await isDuplicate(tempFilePath);
- if (isDup) {
+ // 3. Duplicate Check (CURRENT WORKING FEATURE)
+ console.log('6๏ธโฃ Starting duplicate check...');
+ console.log('๐ Checking for duplicate image...');
+ const duplicateResult = await checkImageHash(tempFilePath);
+ console.log('7๏ธโฃ Duplicate check completed:', duplicateResult);
+
+ // Set hash in log entry
+ logEntry.imageHash = duplicateResult.hash;
+
+ if (duplicateResult.isDuplicate) {
logEntry.reason = 'Duplicate image detected';
- await logValidation(logEntry);
- return NextResponse.json({ validated: false, reason: logEntry.reason }, { status: 400 });
+ console.log('โ Duplicate image detected, rejecting submission');
+
+ // Log the failure if logging is available
+ try {
+ // await logValidation(logEntry); // Uncomment when available
+ } catch (logError) {
+ console.log("โ ๏ธ Logging unavailable:", logError.message);
+ }
+
+ return NextResponse.json({
+ validated: false,
+ reason: logEntry.reason,
+ matchedHash: duplicateResult.matchedHash
+ }, { status: 400 });
}
+
+ console.log('โ
Image is unique, proceeding with validation');
- // 4. Upload metadata to IPFS
- const metadata = {
- constellation: userConstellation,
- latitude,
- longitude,
- timestamp,
- confidence: cnnConfidence,
- image: buffer.toString('base64'),
- };
+ // 4. Upload metadata to IPFS (if user address is provided for minting)
+ let tokenURI = null;
+ if (userAddress) {
+ try {
+ // Uncomment when IPFS is ready
+ // const metadata = {
+ // constellation: userConstellation,
+ // latitude,
+ // longitude,
+ // timestamp,
+ // confidence: logEntry.confidenceScore,
+ // image: buffer.toString('base64'),
+ // };
+ // tokenURI = await uploadMetadataToIPFS(metadata);
+ // logEntry.ipfsMetadataUri = tokenURI;
+ // console.log("โ
Metadata uploaded to IPFS:", tokenURI);
+ console.log("โญ๏ธ IPFS upload skipped (not implemented yet)");
+ } catch (error) {
+ console.log("โ ๏ธ IPFS upload unavailable:", error.message);
+ }
+ }
- const tokenURI = await uploadMetadataToIPFS(metadata);
- logEntry.ipfsMetadataUri = tokenURI;
+ // 5. Mint NFT (if token URI and user address are available)
+ let txHash = null;
+ if (userAddress && tokenURI) {
+ try {
+ // Uncomment when minting is ready
+ // const tx = await mintObservation(userAddress, tokenURI);
+ // txHash = tx.hash;
+ // logEntry.txnHash = txHash;
+ // console.log("โ
NFT minted:", txHash);
+ console.log("โญ๏ธ NFT minting skipped (not implemented yet)");
+ } catch (error) {
+ console.log("โ ๏ธ NFT minting unavailable:", error.message);
+ }
+ }
- // 5. Mint NFT
- const tx = await mintObservation(userAddress, tokenURI);
- logEntry.txnHash = tx.hash;
+ // Mark as valid and log success
logEntry.isValid = true;
- // logEntry.imageHash =
-
- await logValidation(logEntry);
+ logEntry.reason = 'Successfully validated';
+
+ try {
+ // await logValidation(logEntry); // Uncomment when available
+ } catch (logError) {
+ console.log("โ ๏ธ Logging unavailable:", logError.message);
+ }
- return NextResponse.json({
+ console.log('8๏ธโฃ Returning success response...');
+
+ // Return response compatible with both current and future versions
+ const response = {
validated: true,
- txHash: tx.hash,
- tokenURI,
- });
+ message: duplicateResult.mongoEnabled ?
+ 'Image successfully validated and stored in MongoDB' :
+ 'Image validated (MongoDB unavailable)',
+ imageHash: duplicateResult.hash,
+ mongoEnabled: duplicateResult.mongoEnabled || false,
+ savedId: duplicateResult.savedId || null,
+ warning: duplicateResult.warning || null,
+ timestamp: new Date().toISOString()
+ };
+
+ // Add future features to response if they were processed
+ if (txHash) response.txHash = txHash;
+ if (tokenURI) response.tokenURI = tokenURI;
+ if (logEntry.confidenceScore > 0) response.confidenceScore = logEntry.confidenceScore;
+
+ return NextResponse.json(response);
+
} catch (error) {
- console.error('Validation or minting error:', error);
- logEntry.reason = 'Internal server error';
- await logValidation(logEntry);
- return NextResponse.json({ validated: false, reason: logEntry.reason }, { status: 500 });
+ console.error('โ Validation error:', error);
+
+ // Try to log the error if logging is available
+ try {
+ // const errorLogEntry = {
+ // imageHash: '',
+ // isValid: false,
+ // reason: 'Internal server error: ' + error.message,
+ // time: new Date().toISOString()
+ // };
+ // await logValidation(errorLogEntry); // Uncomment when available
+ } catch (logError) {
+ console.log("โ ๏ธ Error logging unavailable:", logError.message);
+ }
+
+ return NextResponse.json({
+ validated: false,
+ reason: 'Internal server error',
+ error: error.message
+ }, { status: 500 });
}
}
diff --git a/web/components/verification.jsx b/web/components/verification.jsx
index 0673469..1ab0cfc 100644
--- a/web/components/verification.jsx
+++ b/web/components/verification.jsx
@@ -6,11 +6,13 @@ const VerificationForm = () => {
const [verificationResult, setVerificationResult] = useState(null);
const [showFailurePopup, setShowFailurePopup] = useState(false);
const [showLoadingPopup, setShowLoadingPopup] = useState(false);
+ const [errorMessage, setErrorMessage] = useState('');
const handleImageChange = async (e) => {
if (e.target.files && e.target.files[0]) {
setSelectedImage(e.target.files[0]);
setVerificationResult(null);
+ setErrorMessage('');
// Show loading popup and start verification
setShowLoadingPopup(true);
@@ -24,28 +26,44 @@ const VerificationForm = () => {
setIsVerifying(true);
try {
- // Simulate verification process
- await new Promise(resolve => setTimeout(resolve, 3000));
-
- // Random success/failure (70% success rate)
- const isSuccess = Math.random() > 0.3;
-
- if (isSuccess) {
- // Mock successful verification result
- const mockResult = {
- timestamp: new Date().toISOString(),
- ipfsHash: 'QmMockHashForDemo123456789'
+ console.log("๐ Starting image verification:", imageFile.name);
+
+ // Call the API directly (no need for separate API client)
+ const formData = new FormData();
+ formData.append('image', imageFile);
+
+ const response = await fetch('/api/validate', {
+ method: 'POST',
+ body: formData,
+ });
+
+ const result = await response.json();
+ console.log("โ
API Response:", result);
+
+ if (response.ok && result.validated) {
+ // Real successful verification result
+ const verificationData = {
+ timestamp: result.timestamp,
+ imageHash: result.imageHash,
+ mongoEnabled: result.mongoEnabled,
+ savedId: result.savedId,
+ ipfsHash: result.imageHash?.substring(0, 20) + "..." || "Generated hash",
+ // Future features
+ txHash: result.txHash,
+ tokenURI: result.tokenURI,
+ confidenceScore: result.confidenceScore,
+ warning: result.warning
};
- setVerificationResult(mockResult);
+
+ setVerificationResult(verificationData);
setShowLoadingPopup(false);
} else {
- // Show failure popup
- setShowLoadingPopup(false);
- setShowFailurePopup(true);
+ throw new Error(result.reason || result.error || 'Validation failed');
}
} catch (error) {
- console.error('Verification error:', error);
+ console.error('โ Verification error:', error);
+ setErrorMessage(error.message || 'Verification failed');
setShowLoadingPopup(false);
setShowFailurePopup(true);
} finally {
@@ -57,6 +75,7 @@ const VerificationForm = () => {
setShowFailurePopup(false);
setSelectedImage(null);
setVerificationResult(null);
+ setErrorMessage('');
};
return (
@@ -117,6 +136,7 @@ const VerificationForm = () => {
accept="image/*"
onChange={handleImageChange}
className="hidden"
+ disabled={isVerifying}
/>
@@ -129,8 +149,39 @@ const VerificationForm = () => {
At: {new Date(verificationResult.timestamp).toLocaleString()}
- IPFS: {verificationResult.ipfsHash}
+ Hash: {verificationResult.imageHash?.substring(0, 16)}...
+
+ {/* Database Status */}
+ {verificationResult.mongoEnabled ? (
+
+ โ
Stored in Database (ID: {verificationResult.savedId?.substring(0, 8)}...)
+
+ ) : (
+
+ โ ๏ธ Database unavailable - using temporary storage
+
+ )}
+
+ {/* Future Features Display */}
+ {verificationResult.txHash && (
+
+ ๐ NFT Minted: {verificationResult.txHash.substring(0, 16)}...
+
+ )}
+
+ {verificationResult.confidenceScore && (
+
+ ๐ง AI Confidence: {(verificationResult.confidenceScore * 100).toFixed(1)}%
+
+ )}
+
+ {verificationResult.warning && (
+
+ โ ๏ธ {verificationResult.warning}
+
+ )}
+
{/* Go to NFT Collection Button */}
{
Verifying...
-
Analyzing constellation pattern
+
Checking for duplicate images
+
Connected to MongoDB database
)}
@@ -162,8 +214,13 @@ const VerificationForm = () => {
โ
Verification Failed
- Image couldnโt be verified. Try another.
+ {errorMessage || "Image couldn't be verified. Try another."}
+ {errorMessage.includes('Duplicate') && (
+
+ This image has already been uploaded to the database.
+
+ )}