From 8bf19016a9adc66e281ae4eb792e62c03f53efcc Mon Sep 17 00:00:00 2001 From: odentas Date: Wed, 10 Dec 2025 15:43:42 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20Ajouter=20route=20API=20d=C3=A9di=C3=A9e?= =?UTF-8?q?=20pour=20t=C3=A9l=C3=A9chargement=20fichiers=20.txt=20depuis?= =?UTF-8?q?=20S3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(app)/vos-documents/page.tsx | 17 +++++--- app/api/documents/download/route.ts | 67 +++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 app/api/documents/download/route.ts diff --git a/app/(app)/vos-documents/page.tsx b/app/(app)/vos-documents/page.tsx index d1741f5..3fc9717 100644 --- a/app/(app)/vos-documents/page.tsx +++ b/app/(app)/vos-documents/page.tsx @@ -468,18 +468,23 @@ function SectionComptables() { const isTxtFile = item.title.toLowerCase().endsWith('.txt') if (isTxtFile) { - // Forcer le téléchargement pour les fichiers .txt en utilisant fetch + // Forcer le téléchargement pour les fichiers .txt via notre API try { - const response = await fetch(docWithUrl.url) - const blob = await response.blob() - const blobUrl = window.URL.createObjectURL(blob) + // Utiliser le storage_path depuis les métadonnées + const storagePath = docWithUrl.meta?.storage_path || item.meta?.storage_path + if (!storagePath) { + alert('Chemin de stockage non disponible') + return + } + + const downloadUrl = `/api/documents/download?path=${encodeURIComponent(storagePath)}` + const link = document.createElement('a') - link.href = blobUrl + link.href = downloadUrl link.download = item.title document.body.appendChild(link) link.click() document.body.removeChild(link) - window.URL.revokeObjectURL(blobUrl) } catch (error) { console.error('Erreur lors du téléchargement:', error) alert('Erreur lors du téléchargement du fichier') diff --git a/app/api/documents/download/route.ts b/app/api/documents/download/route.ts new file mode 100644 index 0000000..d482827 --- /dev/null +++ b/app/api/documents/download/route.ts @@ -0,0 +1,67 @@ +import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs' +import { cookies } from 'next/headers' +import { NextRequest, NextResponse } from 'next/server' +import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' + +export const runtime = 'nodejs' +export const dynamic = 'force-dynamic' + +const s3Client = new S3Client({ + region: process.env.AWS_REGION || 'eu-west-3', + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID || '', + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '', + }, +}) + +export async function GET(request: NextRequest) { + try { + const supabase = createRouteHandlerClient({ cookies }) + + // Vérifier l'authentification + const { data: { session } } = await supabase.auth.getSession() + if (!session) { + return new NextResponse('Unauthorized', { status: 401 }) + } + + const searchParams = request.nextUrl.searchParams + const path = searchParams.get('path') + + if (!path) { + return new NextResponse('Missing path parameter', { status: 400 }) + } + + // Télécharger le fichier depuis S3 + const command = new GetObjectCommand({ + Bucket: process.env.AWS_S3_BUCKET_NAME || '', + Key: path, + }) + + const response = await s3Client.send(command) + + if (!response.Body) { + return new NextResponse('File not found', { status: 404 }) + } + + // Convertir le stream en buffer + const chunks: Uint8Array[] = [] + for await (const chunk of response.Body as any) { + chunks.push(chunk) + } + const buffer = Buffer.concat(chunks) + + // Extraire le nom du fichier depuis le path + const filename = path.split('/').pop() || 'document.txt' + + // Retourner le fichier avec les bons headers pour forcer le téléchargement + return new NextResponse(buffer, { + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'Content-Disposition': `attachment; filename="${filename}"`, + }, + }) + } catch (error) { + console.error('Error in download route:', error) + return new NextResponse('Internal Server Error', { status: 500 }) + } +}