feat: upload creates new chart from filename with duplicate handling
- POST /api/upload now creates a chart named from the CSV filename - Duplicate names get numeric suffix (e.g., btc-daily-2) - Candles inserted with chart_id instead of replacing all data - Response includes chart id and name
This commit is contained in:
parent
b53cb1b7d1
commit
98e91b047a
2 changed files with 41 additions and 10 deletions
|
|
@ -14,10 +14,10 @@
|
||||||
|
|
||||||
## 3. Upload Endpoint Changes
|
## 3. Upload Endpoint Changes
|
||||||
|
|
||||||
- [ ] 3.1 Modify `POST /api/upload` to create a new chart named from the uploaded filename (strip `.csv` extension)
|
- [x] 3.1 Modify `POST /api/upload` to create a new chart named from the uploaded filename (strip `.csv` extension)
|
||||||
- [ ] 3.2 Add duplicate name handling — append numeric suffix if chart name already exists
|
- [x] 3.2 Add duplicate name handling — append numeric suffix if chart name already exists
|
||||||
- [ ] 3.3 Insert candles with the new chart's `chart_id` instead of deleting all existing candles
|
- [x] 3.3 Insert candles with the new chart's `chart_id` instead of deleting all existing candles
|
||||||
- [ ] 3.4 Return chart `id` and `name` in the upload response JSON
|
- [x] 3.4 Return chart `id` and `name` in the upload response JSON
|
||||||
|
|
||||||
## 4. Candles & Annotations API Scoping
|
## 4. Candles & Annotations API Scoping
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,28 @@
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
import Papa from 'papaparse';
|
import Papa from 'papaparse';
|
||||||
import { db } from '@/lib/db';
|
import { db } from '@/lib/db';
|
||||||
import { candles } from '@/lib/db/schema';
|
import { candles, charts } from '@/lib/db/schema';
|
||||||
import { sql } from 'drizzle-orm';
|
import { eq, like } from 'drizzle-orm';
|
||||||
|
|
||||||
|
async function getUniqueChartName(baseName: string): Promise<string> {
|
||||||
|
// Check if the base name is already taken
|
||||||
|
const existing = await db.select().from(charts).where(eq(charts.name, baseName)).limit(1);
|
||||||
|
if (existing.length === 0) return baseName;
|
||||||
|
|
||||||
|
// Find existing charts with this base name pattern (e.g., "btc-daily-2", "btc-daily-3")
|
||||||
|
const pattern = `${baseName}-%`;
|
||||||
|
const suffixed = await db.select({ name: charts.name }).from(charts).where(like(charts.name, pattern));
|
||||||
|
|
||||||
|
let maxSuffix = 1;
|
||||||
|
for (const row of suffixed) {
|
||||||
|
const match = row.name.match(/-(\d+)$/);
|
||||||
|
if (match) {
|
||||||
|
maxSuffix = Math.max(maxSuffix, parseInt(match[1], 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${baseName}-${maxSuffix + 1}`;
|
||||||
|
}
|
||||||
|
|
||||||
export async function POST(request: NextRequest): Promise<NextResponse> {
|
export async function POST(request: NextRequest): Promise<NextResponse> {
|
||||||
try {
|
try {
|
||||||
|
|
@ -18,6 +38,9 @@ export async function POST(request: NextRequest): Promise<NextResponse> {
|
||||||
|
|
||||||
const text = await file.text();
|
const text = await file.text();
|
||||||
|
|
||||||
|
// Derive chart name from filename (strip .csv extension)
|
||||||
|
const baseName = file.name.replace(/\.csv$/i, '');
|
||||||
|
|
||||||
return new Promise<NextResponse>((resolve) => {
|
return new Promise<NextResponse>((resolve) => {
|
||||||
Papa.parse(text, {
|
Papa.parse(text, {
|
||||||
header: true,
|
header: true,
|
||||||
|
|
@ -56,6 +79,15 @@ export async function POST(request: NextRequest): Promise<NextResponse> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get unique chart name (handle duplicates)
|
||||||
|
const chartName = await getUniqueChartName(baseName);
|
||||||
|
|
||||||
|
// Create the chart
|
||||||
|
const [newChart] = await db.insert(charts).values({
|
||||||
|
name: chartName,
|
||||||
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
|
}).returning();
|
||||||
|
|
||||||
// Parse and prepare candle data
|
// Parse and prepare candle data
|
||||||
const candleData = rows.map((row) => {
|
const candleData = rows.map((row) => {
|
||||||
let timestamp: number;
|
let timestamp: number;
|
||||||
|
|
@ -75,6 +107,7 @@ export async function POST(request: NextRequest): Promise<NextResponse> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
chart_id: newChart.id,
|
||||||
time: timestamp,
|
time: timestamp,
|
||||||
open: Number(row.open),
|
open: Number(row.open),
|
||||||
high: Number(row.high),
|
high: Number(row.high),
|
||||||
|
|
@ -83,10 +116,7 @@ export async function POST(request: NextRequest): Promise<NextResponse> {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Delete all old candles before inserting new ones
|
// Insert candles for this chart
|
||||||
await db.delete(candles);
|
|
||||||
|
|
||||||
// Insert new candles
|
|
||||||
const count = candleData.length;
|
const count = candleData.length;
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
await db.insert(candles).values(candleData);
|
await db.insert(candles).values(candleData);
|
||||||
|
|
@ -96,6 +126,7 @@ export async function POST(request: NextRequest): Promise<NextResponse> {
|
||||||
NextResponse.json({
|
NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
count,
|
count,
|
||||||
|
chart: { id: newChart.id, name: newChart.name },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue