From f7bbf2ea5edaffdd7cba0f9767f3f275a1f51832 Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 18 Feb 2026 01:24:49 +0000 Subject: [PATCH] feat: rewrite project delete to use NextAuth session + Postgres --- app/api/projects/delete/route.ts | 106 ++++++++++--------------------- 1 file changed, 35 insertions(+), 71 deletions(-) diff --git a/app/api/projects/delete/route.ts b/app/api/projects/delete/route.ts index ade9fc6..be70b6f 100644 --- a/app/api/projects/delete/route.ts +++ b/app/api/projects/delete/route.ts @@ -1,93 +1,57 @@ import { NextResponse } from 'next/server'; -import { getAdminAuth, getAdminDb } from '@/lib/firebase/admin'; -import { FieldValue } from 'firebase-admin/firestore'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/lib/auth/authOptions'; +import { query } from '@/lib/db-postgres'; -/** - * Delete a project (soft delete - keeps sessions intact) - * Sessions will remain in the database but projectId will be set to null - */ export async function POST(request: Request) { try { - const authHeader = request.headers.get('Authorization'); - if (!authHeader?.startsWith('Bearer ')) { + const session = await getServerSession(authOptions); + if (!session?.user?.email) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } - const idToken = authHeader.split('Bearer ')[1]; - const adminAuth = getAdminAuth(); - const adminDb = getAdminDb(); - - let userId: string; - try { - const decodedToken = await adminAuth.verifyIdToken(idToken); - userId = decodedToken.uid; - } catch (error) { - return NextResponse.json({ error: 'Invalid token' }, { status: 401 }); - } - const { projectId } = await request.json(); - if (!projectId) { - return NextResponse.json( - { error: 'Project ID is required' }, - { status: 400 } - ); + return NextResponse.json({ error: 'Project ID is required' }, { status: 400 }); } - // Verify project belongs to user - const projectDoc = await adminDb.collection('projects').doc(projectId).get(); - - if (!projectDoc.exists) { - return NextResponse.json( - { error: 'Project not found' }, - { status: 404 } - ); + // Verify ownership + const rows = await query<{ id: string; data: any }>(` + SELECT p.id, p.data + FROM fs_projects p + JOIN fs_users u ON u.id = p.user_id + WHERE p.id = $1 AND u.data->>'email' = $2 + LIMIT 1 + `, [projectId, session.user.email]); + + if (rows.length === 0) { + return NextResponse.json({ error: 'Project not found or unauthorized' }, { status: 404 }); } - if (projectDoc.data()?.userId !== userId) { - return NextResponse.json( - { error: 'Unauthorized to delete this project' }, - { status: 403 } - ); - } + // Unlink sessions + const sessionResult = await query(` + SELECT COUNT(*)::int AS count FROM fs_sessions WHERE data->>'projectId' = $1 + `, [projectId]); + const sessionCount = sessionResult[0]?.count || 0; - // Delete the project document - await adminDb.collection('projects').doc(projectId).delete(); + await query(` + UPDATE fs_sessions + SET data = jsonb_set( + jsonb_set(data, '{projectId}', 'null'), + '{needsProjectAssociation}', 'true' + ) + WHERE data->>'projectId' = $1 + `, [projectId]); - // Optional: Update sessions to remove project reference - // This makes sessions "orphaned" but keeps all the data - const sessionsSnapshot = await adminDb - .collection('sessions') - .where('projectId', '==', projectId) - .get(); + // Delete the project + await query(`DELETE FROM fs_projects WHERE id = $1`, [projectId]); - if (!sessionsSnapshot.empty) { - const batch = adminDb.batch(); - sessionsSnapshot.docs.forEach((doc) => { - batch.update(doc.ref, { - projectId: null, - // Flag these as needing reassignment if user wants to link them later - needsProjectAssociation: true, - updatedAt: FieldValue.serverTimestamp(), - }); - }); - await batch.commit(); - } - - return NextResponse.json({ - success: true, - message: 'Project deleted successfully', - sessionsPreserved: sessionsSnapshot.size, - }); + return NextResponse.json({ success: true, message: 'Project deleted successfully', sessionsPreserved: sessionCount }); } catch (error) { - console.error('[Project Delete] Error:', error); + console.error('[POST /api/projects/delete] Error:', error); return NextResponse.json( - { - error: 'Failed to delete project', - details: error instanceof Error ? error.message : String(error), - }, + { error: 'Failed to delete project', details: error instanceof Error ? error.message : String(error) }, { status: 500 } ); } } -