- Remove better-sqlite3, add pg driver - Convert schema to PostgreSQL types (serial, timestamp, boolean, jsonb) - Generate fresh PostgreSQL migrations - Update database connection layer with pg.Pool - Fix all API routes: remove JSON.parse/stringify, use native timestamps and booleans - Update drizzle.config.ts and .env.example for PostgreSQL
71 lines
2.2 KiB
TypeScript
71 lines
2.2 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { db } from '@/lib/db';
|
|
import { annotations, candles, charts } from '@/lib/db/schema';
|
|
import { eq, and, desc } from 'drizzle-orm';
|
|
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
const { searchParams } = request.nextUrl;
|
|
let chartId = searchParams.get('chartId');
|
|
|
|
// Fall back to most recent chart if no chartId provided
|
|
if (!chartId) {
|
|
const latest = await db.select({ id: charts.id }).from(charts).orderBy(desc(charts.created_at)).limit(1);
|
|
if (latest.length === 0) {
|
|
return new NextResponse('timestamp,label_type,price\n', {
|
|
headers: {
|
|
'Content-Type': 'text/csv',
|
|
'Content-Disposition': 'attachment; filename="annotations.csv"',
|
|
},
|
|
});
|
|
}
|
|
chartId = String(latest[0].id);
|
|
}
|
|
|
|
const chartIdNum = parseInt(chartId, 10);
|
|
const allAnnotations = await db
|
|
.select()
|
|
.from(annotations)
|
|
.where(eq(annotations.chart_id, chartIdNum));
|
|
|
|
// Build CSV content
|
|
const csvRows = ['timestamp,label_type,price'];
|
|
|
|
for (const annotation of allAnnotations) {
|
|
let price: number | null = null;
|
|
|
|
if (annotation.label_type === 'break_up' || annotation.label_type === 'break_down') {
|
|
const candleResult = await db
|
|
.select()
|
|
.from(candles)
|
|
.where(and(eq(candles.chart_id, chartIdNum), eq(candles.time, annotation.timestamp)))
|
|
.limit(1);
|
|
|
|
if (candleResult.length > 0) {
|
|
price = candleResult[0].close;
|
|
}
|
|
} else if (annotation.label_type === 'line' && annotation.geometry) {
|
|
const geometry = annotation.geometry as any;
|
|
price = geometry.startPrice || null;
|
|
}
|
|
|
|
csvRows.push(
|
|
`${annotation.timestamp},${annotation.label_type},${price !== null ? price : ''}`
|
|
);
|
|
}
|
|
|
|
const csvContent = csvRows.join('\n');
|
|
|
|
return new NextResponse(csvContent, {
|
|
headers: {
|
|
'Content-Type': 'text/csv',
|
|
'Content-Disposition': 'attachment; filename="annotations.csv"',
|
|
},
|
|
});
|
|
} catch (error: any) {
|
|
return NextResponse.json(
|
|
{ error: error.message || 'Failed to export annotations' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|