feat: scope candles/annotations/export APIs by chartId query param
- GET /api/candles accepts ?chartId= with fallback to most recent chart - GET /api/annotations accepts ?chartId= with fallback to most recent chart - POST /api/annotations now requires chart_id in request body - GET /api/export accepts ?chartId= to scope exported annotations - DELETE /api/annotations supports optional chartId scoping
This commit is contained in:
parent
98e91b047a
commit
90e1e179cc
4 changed files with 107 additions and 35 deletions
|
|
@ -1,11 +1,32 @@
|
|||
import { NextResponse } from 'next/server';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
import { annotations, candles } from '@/lib/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { annotations, candles, charts } from '@/lib/db/schema';
|
||||
import { eq, and, desc } from 'drizzle-orm';
|
||||
|
||||
export async function GET() {
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const allAnnotations = await db.select().from(annotations);
|
||||
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'];
|
||||
|
|
@ -14,18 +35,16 @@ export async function GET() {
|
|||
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))
|
||||
.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) {
|
||||
// For line annotations, use startPrice from geometry
|
||||
const geometry = JSON.parse(annotation.geometry);
|
||||
price = geometry.startPrice || null;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue