✅ Restore working dashboard and TradingView analysis
- 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
This commit is contained in:
56
app/api/image/route.js
Normal file
56
app/api/image/route.js
Normal file
@@ -0,0 +1,56 @@
|
||||
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 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user