feat: implement backend API endpoints

- CSV upload with papaparse (handles date strings and Unix timestamps)
- Annotations CRUD (GET, POST, DELETE)
- Candles GET endpoint
- Export annotations as CSV
This commit is contained in:
Marko Djordjevic 2026-02-12 10:24:03 +01:00
parent d04b673cfa
commit 096a80b229
5 changed files with 312 additions and 0 deletions

View file

@ -0,0 +1,52 @@
import { NextResponse } from 'next/server';
import { db } from '@/lib/db';
import { annotations, candles } from '@/lib/db/schema';
import { eq } from 'drizzle-orm';
export async function GET() {
try {
const allAnnotations = await db.select().from(annotations);
// 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') {
// For marker annotations, look up the candle's close price
const candleResult = await db
.select()
.from(candles)
.where(eq(candles.time, annotation.timestamp))
.limit(1);
if (candleResult.length > 0) {
price = candleResult[0].close;
}
} else if (annotation.label_type === 'line' && annotation.geometry) {
// For line annotations, use startPrice from geometry
const geometry = JSON.parse(annotation.geometry);
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 }
);
}
}