try now
Some checks failed
Publish Ranger template to Laconic Registry / laconic_publish (push) Failing after 1m5s

This commit is contained in:
zramsay 2025-02-02 18:05:49 -05:00
parent 2649cee033
commit 9a44248f51
3 changed files with 105 additions and 68 deletions

View File

@ -1,24 +1,45 @@
// src/app/api/analyze/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { analyzeImageWithVision } from '../../../services/googleVisionCore'
import { processAnimalImage } from '../../../services/animalProcessingService'
// Increase body parser size limit
export const config = {
api: {
bodyParser: {
sizeLimit: '10mb'
}
}
}
export async function POST(req: NextRequest): Promise<NextResponse> {
try {
// Log incoming request details
console.log('Incoming request:', {
method: req.method,
contentType: req.headers.get('content-type'),
contentLength: req.headers.get('content-length')
})
// Parse form data
const formData = await req.formData()
const imageFile = formData.get('image')
// Robust type checking
// Validate image file
if (!imageFile || !(imageFile instanceof File)) {
console.error('Invalid file upload:', {
imageFile: imageFile ? 'Exists' : 'Not found',
type: imageFile ? typeof imageFile : 'undefined'
})
return NextResponse.json(
{ error: 'No valid image provided' },
{ status: 400 }
)
}
// Log file details for debugging
console.log('Received file:', {
// Log file details
console.log('Uploaded file details:', {
name: imageFile.name,
type: imageFile.type,
size: imageFile.size
@ -28,23 +49,42 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
const arrayBuffer = await imageFile.arrayBuffer()
const buffer = Buffer.from(arrayBuffer)
// Get vision analysis
// Log buffer details
console.log('Buffer details:', {
bufferLength: buffer.length
})
// Validate buffer size
const MAX_BUFFER_SIZE = 10 * 1024 * 1024 // 10MB
if (buffer.length > MAX_BUFFER_SIZE) {
console.error('Buffer too large:', {
bufferLength: buffer.length,
maxAllowedSize: MAX_BUFFER_SIZE
})
return NextResponse.json(
{ error: 'Image file is too large' },
{ status: 413 }
)
}
// Analyze image
const visionResult = await analyzeImageWithVision(buffer)
// Construct the response message
// Construct response message
const responseMessage = `${visionResult.description}\n\n${
visionResult.isAnimal
? "✨ This image contains wildlife and has been added to our registry! Thank you for contributing to our wildlife database."
: "🌿 No wildlife detected in this image. Try uploading a photo of an animal!"
}`
// Send response to user
// Prepare user response
const userResponse = NextResponse.json({
description: responseMessage,
isAnimal: visionResult.isAnimal
})
// If animal detected, process in background
// Background processing for animal images
if (visionResult.isAnimal) {
processAnimalImage(
buffer,
@ -57,7 +97,14 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
return userResponse
} catch (error) {
console.error('Analysis failed:', error)
// Comprehensive error logging
console.error('Analysis failed:', {
errorName: error instanceof Error ? error.name : 'Unknown Error',
errorMessage: error instanceof Error ? error.message : String(error),
errorStack: error instanceof Error ? error.stack : 'No stack trace'
})
// Return user-friendly error response
return NextResponse.json(
{ error: 'Failed to analyze image' },
{ status: 500 }
@ -65,4 +112,5 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
}
}
// Ensure dynamic routing
export const dynamic = 'force-dynamic'

View File

@ -23,22 +23,19 @@ export const VISION_CONFIG: VisionConfig = {
export async function analyzeImage(imageBuffer: Buffer, filename: string): Promise<VisionAnalysisResult> {
try {
// Create FormData using more universal approach
const formData = new FormData()
// Create file from buffer using Blob
// Create Blob with explicit JPEG type
const blob = new Blob([imageBuffer], { type: 'image/jpeg' })
// Detailed logging for debugging
console.log('Analyzing image:', {
filename,
bufferLength: imageBuffer.length,
blobSize: blob.size,
blobType: blob.type
})
formData.append('image', blob, filename)
// Use File constructor for maximum compatibility
const file = new File([blob], filename, { type: 'image/jpeg' })
// Log FormData contents (if possible)
// Append file to FormData
formData.append('image', file, filename)
// Log FormData contents for debugging
for (const [key, value] of formData.entries()) {
console.log('FormData entry:', key, value)
}
@ -48,23 +45,19 @@ export async function analyzeImage(imageBuffer: Buffer, filename: string): Promi
body: formData
})
// Log full response for debugging
const responseText = await response.text()
console.log('Raw response:', responseText)
if (!response.ok) {
try {
const errorData = JSON.parse(responseText)
throw new Error(errorData.error || 'Failed to analyze image')
} catch {
throw new Error(`HTTP error ${response.status}: ${responseText}`)
}
// More detailed error handling
const errorText = await response.text()
console.error('Analysis response error:', {
status: response.status,
statusText: response.statusText,
errorText
})
throw new Error(errorText || 'Failed to analyze image')
}
// Parse response
const result = JSON.parse(responseText)
return result
return await response.json()
} catch (error) {
console.error('Vision analysis error:', error)
return {

View File

@ -1,6 +1,6 @@
// src/services/imageService.ts
import imageCompression from 'browser-image-compression'
import * as FileType from 'file-type'
import { analyzeImage, VisionAnalysisResult } from './googleVisionService'
export interface ImageConversionOptions {
maxSizeMB?: number;
@ -13,48 +13,44 @@ export async function normalizeImageUpload(
options: ImageConversionOptions = {}
): Promise<File> {
try {
// Default conversion options
// Detailed initial logging
console.log('Original File Details:', {
name: file.name,
type: file.type,
size: file.size
})
// Default conversion options with cross-browser considerations
const defaultOptions = {
maxSizeMB: 2, // Max file size
maxWidthOrHeight: 1920, // Max image dimensions
maxSizeMB: 2, // Keep original size limit
maxWidthOrHeight: 1920, // Standard max dimension
useWebWorker: true
}
const mergedOptions = { ...defaultOptions, ...options }
// Step 1: Detect file type
const detectedType = await FileType.fileTypeFromBlob(file)
console.log('Detected file type:', detectedType)
// List of supported input types
const supportedTypes = [
'image/jpeg',
'image/png',
'image/gif',
'image/webp',
'image/heif',
'image/heic'
]
// Validate file type
if (!supportedTypes.includes(file.type) &&
!(detectedType && supportedTypes.includes(detectedType.mime))) {
throw new Error('Unsupported image type')
}
// Step 2: Compress image
// Force conversion to ensure compatibility
const compressedFile = await imageCompression(file, {
maxSizeMB: mergedOptions.maxSizeMB,
maxWidthOrHeight: mergedOptions.maxWidthOrHeight,
useWebWorker: mergedOptions.useWebWorker,
// Explicitly convert to JPEG by forcing type conversion
initialQuality: 0.8, // Add some quality control
...mergedOptions,
fileType: 'image/jpeg', // Force JPEG
initialQuality: 0.8
})
// Step 3: Create a new JPEG file
return new File([compressedFile], 'converted-image.jpg', {
type: 'image/jpeg'
// Create a new File object with explicit JPEG type
const normalizedImage = new File(
[compressedFile],
'converted-image.jpg',
{ type: 'image/jpeg' }
)
// Logging normalized file details
console.log('Normalized File Details:', {
name: normalizedImage.name,
type: normalizedImage.type,
size: normalizedImage.size
})
return normalizedImage
} catch (error) {
console.error('Image normalization error:', error)
throw error