project-standalo-note-to-app/app/api/recordings/route.ts

126 lines
3.8 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import prisma from '@/lib/prisma';
import { getCurrentUser } from '@/lib/auth';
import type { ListRecordingsResponse, CreateRecordingResponse, CreateRecordingError400 } from '@/types/api-types';
export async function GET(request: NextRequest) {
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { searchParams } = new URL(request.url);
const limit = parseInt(searchParams.get('limit') || '50');
const offset = parseInt(searchParams.get('offset') || '0');
const [recordings, total] = await Promise.all([
prisma.recording.findMany({
where: { userId: user.id },
orderBy: { createdAt: 'desc' },
skip: offset,
take: limit,
select: {
id: true,
title: true,
audioFilePath: true,
duration: true,
transcript: true,
summary: true,
isTranscribing: true,
createdAt: true,
updatedAt: true,
}
}),
prisma.recording.count({
where: { userId: user.id }
})
]);
const response: ListRecordingsResponse = {
recordings: recordings.map(rec => ({
id: rec.id,
title: rec.title,
audioFilePath: rec.audioFilePath,
duration: rec.duration,
transcript: rec.transcript || undefined,
summary: rec.summary || undefined,
isTranscribing: rec.isTranscribing,
createdAt: rec.createdAt.toISOString(),
})),
total,
};
return NextResponse.json(response, { status: 200 });
} catch (error) {
console.error('List recordings error:', error);
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
}
}
export async function POST(request: NextRequest) {
try {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const formData = await request.formData();
const audioFile = formData.get('audio') as File | null;
const title = formData.get('title') as string | null;
const durationStr = formData.get('duration') as string | null;
// Validate required fields
if (!audioFile || !durationStr) {
const error: CreateRecordingError400 = { error: 'Audio file and duration are required' };
return NextResponse.json(error, { status: 400 });
}
const duration = parseInt(durationStr);
if (isNaN(duration) || duration < 1) {
const error: CreateRecordingError400 = { error: 'Invalid duration' };
return NextResponse.json(error, { status: 400 });
}
// Generate filename
const timestamp = Date.now();
const extension = audioFile.name.split('.').pop() || 'webm';
const filename = `${user.id}/${timestamp}.${extension}`;
const audioFilePath = `recordings/${filename}`;
// TODO: Upload to MinIO/S3 storage
// For now, we'll just store the path
// Create recording
const recording = await prisma.recording.create({
data: {
userId: user.id,
title: title || `Recording ${new Date().toISOString()}`,
audioFilePath,
duration,
isTranscribing: false,
},
select: {
id: true,
title: true,
audioFilePath: true,
duration: true,
createdAt: true,
}
});
const response: CreateRecordingResponse = {
id: recording.id,
title: recording.title,
audioFilePath: recording.audioFilePath,
duration: recording.duration,
createdAt: recording.createdAt.toISOString(),
};
return NextResponse.json(response, { status: 201 });
} catch (error) {
console.error('Create recording error:', error);
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
}
}