- Fixed layout conflicts by removing minimal layout.tsx in favor of complete layout.js - Restored original AI Analysis page with full TradingView integration - Connected enhanced screenshot API to real TradingView automation service - Fixed screenshot gallery to handle both string and object formats - Added image serving API route for screenshot display - Resolved hydration mismatch issues with suppressHydrationWarning - All navigation pages working (Analysis, Trading, Automation, Settings) - TradingView automation successfully capturing screenshots from AI and DIY layouts - Docker Compose v2 compatibility ensured Working features: - Homepage with hero section and status cards - Navigation menu with Trading Bot branding - Real TradingView screenshot capture - AI-powered chart analysis - Multi-layout support (AI + DIY module) - Screenshot gallery with image serving - API endpoints for balance, status, screenshots, trading
57 lines
1.7 KiB
JavaScript
57 lines
1.7 KiB
JavaScript
import { NextResponse } from 'next/server'
|
|
import { promises as fs } from 'fs'
|
|
import path from 'path'
|
|
|
|
export async function GET(request) {
|
|
try {
|
|
const { searchParams } = new URL(request.url)
|
|
const filename = searchParams.get('file')
|
|
|
|
if (!filename) {
|
|
return NextResponse.json({ error: 'Filename parameter required' }, { status: 400 })
|
|
}
|
|
|
|
// Security: prevent path traversal
|
|
const sanitizedFilename = path.basename(filename)
|
|
const screenshotsDir = path.join(process.cwd(), 'screenshots')
|
|
const filePath = path.join(screenshotsDir, sanitizedFilename)
|
|
|
|
try {
|
|
const imageBuffer = await fs.readFile(filePath)
|
|
|
|
// Determine content type based on file extension
|
|
const ext = path.extname(sanitizedFilename).toLowerCase()
|
|
let contentType = 'image/png' // default
|
|
|
|
switch (ext) {
|
|
case '.jpg':
|
|
case '.jpeg':
|
|
contentType = 'image/jpeg'
|
|
break
|
|
case '.png':
|
|
contentType = 'image/png'
|
|
break
|
|
case '.svg':
|
|
contentType = 'image/svg+xml'
|
|
break
|
|
case '.webp':
|
|
contentType = 'image/webp'
|
|
break
|
|
}
|
|
|
|
return new NextResponse(imageBuffer, {
|
|
headers: {
|
|
'Content-Type': contentType,
|
|
'Cache-Control': 'public, max-age=3600', // Cache for 1 hour
|
|
},
|
|
})
|
|
} catch (fileError) {
|
|
console.error('Image file not found:', filePath)
|
|
return NextResponse.json({ error: 'Image not found' }, { status: 404 })
|
|
}
|
|
} catch (error) {
|
|
console.error('Image API error:', error)
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
|
}
|
|
}
|