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:
mindesbunister
2025-07-14 14:21:19 +02:00
parent 9978760995
commit de45349baa
68 changed files with 2147 additions and 1813 deletions

View File

@@ -1,11 +0,0 @@
import { NextResponse } from 'next/server'
import { driftTradingService } from '../../../../lib/drift-trading'
export async function GET() {
try {
const balance = await driftTradingService.getTradingBalance()
return NextResponse.json(balance)
} catch (error: any) {
return NextResponse.json({ error: error.message }, { status: 500 })
}
}

View File

@@ -1,46 +0,0 @@
import { NextResponse } from 'next/server'
import { driftTradingService } from '../../../../lib/drift-trading'
export async function POST(request: Request) {
try {
const { symbol, amount } = await request.json()
console.log(`🔒 Close position request: ${amount || 'ALL'} ${symbol}`)
// Validate inputs
if (!symbol) {
return NextResponse.json(
{ success: false, error: 'Symbol is required' },
{ status: 400 }
)
}
// Execute position close
const result = await driftTradingService.closePosition(symbol, amount)
if (result.success) {
console.log(`✅ Position closed successfully: ${result.txId}`)
return NextResponse.json({
success: true,
txId: result.txId,
message: `Position in ${symbol} closed successfully`
})
} else {
console.error(`❌ Failed to close position: ${result.error}`)
return NextResponse.json(
{ success: false, error: result.error },
{ status: 500 }
)
}
} catch (error: any) {
console.error('❌ Close position API error:', error)
return NextResponse.json(
{
success: false,
error: error.message || 'Failed to close position'
},
{ status: 500 }
)
}
}

View File

@@ -1,29 +0,0 @@
import { NextRequest, NextResponse } from 'next/server'
import { driftTradingService } from '../../../../lib/drift-trading'
export async function GET(request: NextRequest) {
try {
const status = await driftTradingService.getDataAvailabilityStatus()
return NextResponse.json(status)
} catch (error) {
console.error('Error getting data status:', error)
// Return fallback status
return NextResponse.json({
status: 'Error Checking Status',
sources: [
{
name: 'System Check',
available: false,
description: 'Unable to check data source availability'
}
],
recommendations: [
'Try refreshing the page',
'Check your internet connection',
'Contact support if the issue persists'
]
})
}
}

View File

@@ -1,19 +0,0 @@
import { NextResponse } from 'next/server'
import { driftTradingService } from '../../../../lib/drift-trading'
export async function POST() {
try {
const loginStatus = await driftTradingService.login()
return NextResponse.json(loginStatus)
} catch (error: any) {
return NextResponse.json(
{
isLoggedIn: false,
publicKey: '',
userAccountExists: false,
error: error.message
},
{ status: 500 }
)
}
}

View File

@@ -1,11 +0,0 @@
import { NextResponse } from 'next/server'
import { driftTradingService } from '../../../../lib/drift-trading'
export async function GET() {
try {
const positions = await driftTradingService.getPositions()
return NextResponse.json({ positions })
} catch (error: any) {
return NextResponse.json({ error: error.message }, { status: 500 })
}
}

View File

@@ -1,90 +0,0 @@
import { NextRequest, NextResponse } from 'next/server'
import { driftTradingService } from '../../../../lib/drift-trading'
export async function POST(request: NextRequest) {
try {
const { action } = await request.json()
if (action === 'start') {
console.log('🚀 Starting real-time monitoring...')
const result = await driftTradingService.startRealtimeMonitoring()
if (result.success) {
return NextResponse.json({
success: true,
message: 'Real-time monitoring started successfully',
status: driftTradingService.getRealtimeMonitoringStatus()
})
} else {
return NextResponse.json({
success: false,
error: result.error,
message: 'Failed to start real-time monitoring'
}, { status: 500 })
}
} else if (action === 'stop') {
console.log('🛑 Stopping real-time monitoring...')
await driftTradingService.stopRealtimeMonitoring()
return NextResponse.json({
success: true,
message: 'Real-time monitoring stopped',
status: driftTradingService.getRealtimeMonitoringStatus()
})
} else if (action === 'status') {
const status = driftTradingService.getRealtimeMonitoringStatus()
return NextResponse.json({
success: true,
status,
message: status.isActive ? 'Real-time monitoring is active' : 'Real-time monitoring is not active'
})
} else if (action === 'clear') {
driftTradingService.clearRealtimeTradesCache()
return NextResponse.json({
success: true,
message: 'Real-time trades cache cleared',
status: driftTradingService.getRealtimeMonitoringStatus()
})
} else {
return NextResponse.json({
success: false,
error: 'Invalid action. Use: start, stop, status, or clear'
}, { status: 400 })
}
} catch (error: any) {
console.error('❌ Error in realtime monitoring endpoint:', error)
return NextResponse.json({
success: false,
error: error.message,
message: 'Internal server error'
}, { status: 500 })
}
}
export async function GET(request: NextRequest) {
try {
const status = driftTradingService.getRealtimeMonitoringStatus()
return NextResponse.json({
success: true,
status,
message: status.isActive ? 'Real-time monitoring is active' : 'Real-time monitoring is not active'
})
} catch (error: any) {
console.error('❌ Error getting monitoring status:', error)
return NextResponse.json({
success: false,
error: error.message
}, { status: 500 })
}
}

View File

@@ -1,44 +0,0 @@
import { NextResponse } from 'next/server'
import { driftTradingService } from '../../../../lib/drift-trading'
export async function POST(request: Request) {
try {
console.log('🔄 API: Manually syncing trades with Drift...')
// Get current positions to check for any changes
const positions = await driftTradingService.getPositions()
// Check for recent closures that might not be in history yet
const recentClosures = await driftTradingService.getRecentClosures(24)
// Get existing trading history
const existingTrades = await driftTradingService.getTradingHistory(100)
console.log(`📊 Found ${positions.length} active positions`)
console.log(`📊 Found ${recentClosures.length} recent closures`)
console.log(`📊 Found ${existingTrades.length} existing trades`)
return NextResponse.json({
success: true,
message: 'Trade sync completed',
data: {
activePositions: positions.length,
recentClosures: recentClosures.length,
existingTrades: existingTrades.length,
positions: positions,
closures: recentClosures
}
})
} catch (error: any) {
console.error('❌ API: Error syncing trades:', error)
return NextResponse.json(
{
success: false,
error: error.message,
message: 'Failed to sync trades. Please try again.'
},
{ status: 500 }
)
}
}

View File

@@ -1,105 +0,0 @@
import { NextResponse } from 'next/server'
import { driftTradingService } from '../../../../lib/drift-trading'
export async function POST(request: Request) {
try {
const {
symbol,
side,
amount,
leverage,
orderType,
price,
stopLoss,
takeProfit,
stopLossType,
takeProfitType
} = await request.json()
console.log(`🎯 Trade request: ${side} ${amount} ${symbol} at ${leverage}x leverage`)
if (stopLoss) console.log(`🛑 Stop Loss: $${stopLoss} (${stopLossType})`)
if (takeProfit) console.log(`🎯 Take Profit: $${takeProfit} (${takeProfitType})`)
// Validate inputs
if (!symbol || !side || !amount || !leverage) {
return NextResponse.json(
{ success: false, error: 'Missing required trade parameters' },
{ status: 400 }
)
}
if (amount <= 0 || leverage < 1 || leverage > 20) {
return NextResponse.json(
{ success: false, error: 'Invalid trade parameters' },
{ status: 400 }
)
}
// Validate stop loss and take profit if provided
if (stopLoss && stopLoss <= 0) {
return NextResponse.json(
{ success: false, error: 'Invalid stop loss price' },
{ status: 400 }
)
}
if (takeProfit && takeProfit <= 0) {
return NextResponse.json(
{ success: false, error: 'Invalid take profit price' },
{ status: 400 }
)
}
// Convert LONG/SHORT to BUY/SELL for the trading service
const tradeSide: 'BUY' | 'SELL' = side === 'LONG' ? 'BUY' : 'SELL'
// Execute trade
const tradeParams = {
symbol,
side: tradeSide,
amount, // Position size in tokens
orderType: orderType || 'MARKET',
price: orderType === 'LIMIT' ? price : undefined,
stopLoss,
takeProfit,
stopLossType,
takeProfitType
}
const result = await driftTradingService.executeTrade(tradeParams)
if (result.success) {
console.log(`✅ Trade executed successfully: ${result.txId}`)
const response: any = {
success: true,
txId: result.txId,
executedPrice: result.executedPrice,
executedAmount: result.executedAmount,
message: `${side} order for ${amount} ${symbol} executed successfully`
}
if (result.conditionalOrders && result.conditionalOrders.length > 0) {
response.conditionalOrders = result.conditionalOrders
response.message += ` with ${result.conditionalOrders.length} conditional order(s)`
}
return NextResponse.json(response)
} else {
console.error(`❌ Trade execution failed: ${result.error}`)
return NextResponse.json(
{ success: false, error: result.error },
{ status: 500 }
)
}
} catch (error: any) {
console.error('❌ Trade API error:', error)
return NextResponse.json(
{
success: false,
error: error.message || 'Trade execution failed'
},
{ status: 500 }
)
}
}

View File

@@ -1,46 +0,0 @@
import { NextResponse } from 'next/server'
import { driftTradingService } from '../../../../lib/drift-trading'
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url)
const limit = parseInt(searchParams.get('limit') || '50')
console.log('📊 API: Getting Drift trading history...')
const tradingHistory = await driftTradingService.getTradingHistory(limit)
// If no trades found, provide helpful message
if (tradingHistory.length === 0) {
console.log('⚠️ No trading history found')
return NextResponse.json({
success: true,
trades: [],
count: 0,
message: 'No trading history found. If you recently closed positions, they may take some time to appear in history.'
})
}
console.log(`✅ Successfully fetched ${tradingHistory.length} trades`)
return NextResponse.json({
success: true,
trades: tradingHistory,
count: tradingHistory.length,
message: `Found ${tradingHistory.length} trade(s)`
})
} catch (error: any) {
console.error('❌ API: Error getting trading history:', error)
return NextResponse.json(
{
success: false,
error: error.message,
trades: [],
count: 0,
message: 'Failed to fetch trading history. Please try again.'
},
{ status: 500 }
)
}
}

View File

@@ -1,122 +0,0 @@
import { NextRequest, NextResponse } from 'next/server'
import { driftTradingService } from '../../../../lib/drift-trading'
export async function POST(request: NextRequest) {
try {
const { symbol, side, leverage } = await request.json()
console.log(`📊 Calculating trade requirements for ${symbol} ${side} with ${leverage}x leverage`)
// Get current account balance
const balance = await driftTradingService.getAccountBalance()
// Get current market price for the symbol
let marketPrice = 160 // Default SOL price
try {
// You could get real market price here from Drift or other price feeds
if (symbol === 'SOLUSD') {
marketPrice = 160 // Could be fetched from oracle
} else if (symbol === 'BTCUSD') {
marketPrice = 65000
} else if (symbol === 'ETHUSD') {
marketPrice = 3500
}
} catch (priceError) {
console.log('⚠️ Could not get market price, using default')
}
// Calculate position limits based on available collateral
const availableCollateral = balance.freeCollateral || balance.availableBalance || 0
const maxLeveragedValue = availableCollateral * (leverage || 1)
// Calculate max position size in tokens
const maxPositionSize = marketPrice > 0 ? maxLeveragedValue / marketPrice : 0
// Calculate margin requirement for this position size
const marginRequirement = maxLeveragedValue / (leverage || 1)
// Calculate estimated liquidation price (simplified)
const maintenanceMarginRatio = 0.05 // 5% maintenance margin
let estimatedLiquidationPrice = 0
if (side.toUpperCase() === 'LONG') {
estimatedLiquidationPrice = marketPrice * (1 - (1 / leverage) + maintenanceMarginRatio)
} else {
estimatedLiquidationPrice = marketPrice * (1 + (1 / leverage) - maintenanceMarginRatio)
}
const tradingCalculations = {
marketPrice,
availableCollateral,
maxPositionSize,
maxLeveragedValue,
marginRequirement,
estimatedLiquidationPrice,
leverage: leverage || 1,
symbol,
side: side.toUpperCase()
}
console.log(`📊 Trading calculations:`, tradingCalculations)
return NextResponse.json({
success: true,
calculations: tradingCalculations,
balance: {
totalCollateral: balance.totalCollateral,
freeCollateral: balance.freeCollateral,
availableBalance: balance.availableBalance,
marginRequirement: balance.marginRequirement,
netUsdValue: balance.netUsdValue
}
})
} catch (error: any) {
console.error('❌ Error calculating trade requirements:', error)
return NextResponse.json({
success: false,
error: error.message,
calculations: {
marketPrice: 0,
availableCollateral: 0,
maxPositionSize: 0,
maxLeveragedValue: 0,
marginRequirement: 0,
estimatedLiquidationPrice: 0,
leverage: 1,
symbol: 'UNKNOWN',
side: 'BUY'
}
}, { status: 500 })
}
}
export async function GET(request: NextRequest) {
try {
// Return basic trading info without specific calculations
const balance = await driftTradingService.getAccountBalance()
return NextResponse.json({
success: true,
balance: {
totalCollateral: balance.totalCollateral,
freeCollateral: balance.freeCollateral,
availableBalance: balance.availableBalance,
marginRequirement: balance.marginRequirement,
netUsdValue: balance.netUsdValue,
leverage: balance.leverage,
unrealizedPnl: balance.unrealizedPnl
},
message: 'Account balance retrieved successfully'
})
} catch (error: any) {
console.error('❌ Error getting trading info:', error)
return NextResponse.json({
success: false,
error: error.message
}, { status: 500 })
}
}

View File

@@ -1,95 +0,0 @@
import { NextResponse } from 'next/server'
import { Connection, PublicKey, Keypair } from '@solana/web3.js'
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url)
const limit = parseInt(searchParams.get('limit') || '50')
console.log('📊 API: Getting Solana transaction history...')
// Get private key from environment
const privateKeyString = process.env.PRIVATE_KEY
if (!privateKeyString) {
throw new Error('PRIVATE_KEY not found in environment variables')
}
// Convert private key to Keypair
const privateKeyBytes = JSON.parse(privateKeyString)
const keypair = Keypair.fromSecretKey(new Uint8Array(privateKeyBytes))
// Connect to Helius RPC
const connection = new Connection(process.env.HELIUS_RPC_ENDPOINT || 'https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY')
// Get transaction signatures for this wallet
const signatures = await connection.getSignaturesForAddress(
keypair.publicKey,
{ limit: limit * 2 } // Get more signatures to filter for Drift transactions
)
console.log(`🔍 Found ${signatures.length} total signatures`)
// Get transaction details for each signature
const transactions = []
const driftProgramId = 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH' // Drift program ID
for (const sig of signatures.slice(0, limit)) {
try {
const tx = await connection.getTransaction(sig.signature, {
maxSupportedTransactionVersion: 0
})
if (!tx) continue
// Check if this transaction involves the Drift program
const isDriftTransaction = tx.transaction.message.staticAccountKeys?.some(
key => key.toString() === driftProgramId
) || tx.transaction.message.compiledInstructions?.some(
instruction => {
const programKey = tx.transaction.message.staticAccountKeys?.[instruction.programIdIndex]
return programKey?.toString() === driftProgramId
}
)
if (isDriftTransaction) {
// Parse the transaction to extract trading information
const blockTime = tx.blockTime ? new Date(tx.blockTime * 1000) : new Date()
transactions.push({
id: sig.signature,
signature: sig.signature,
blockTime: blockTime.toISOString(),
slot: tx.slot,
status: sig.err ? 'FAILED' : 'SUCCESS',
fee: tx.meta?.fee || 0,
// Try to extract more details from logs
logs: tx.meta?.logMessages?.slice(0, 5) || [],
accounts: tx.transaction.message.staticAccountKeys?.slice(0, 10).map(k => k.toString()) || []
})
}
} catch (txError) {
console.log(`⚠️ Failed to get transaction ${sig.signature}:`, txError)
}
}
console.log(`📊 Found ${transactions.length} Drift transactions`)
return NextResponse.json({
success: true,
transactions,
count: transactions.length,
totalSignatures: signatures.length
})
} catch (error: any) {
console.error('❌ API: Error getting transaction history:', error)
return NextResponse.json(
{
success: false,
error: error.message,
transactions: []
},
{ status: 500 }
)
}
}