194 lines
No EOL
6.4 KiB
TypeScript
194 lines
No EOL
6.4 KiB
TypeScript
// app/api/staff/facturation/[id]/upload-pdf/route.ts
|
|
import { NextResponse } from "next/server";
|
|
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
|
|
import { cookies } from "next/headers";
|
|
|
|
export const dynamic = 'force-dynamic';
|
|
export const revalidate = 0;
|
|
export const runtime = 'nodejs';
|
|
|
|
async function isStaffUser(supabase: any, userId: string): Promise<boolean> {
|
|
try {
|
|
const { data: staffRow } = await supabase
|
|
.from('staff_users')
|
|
.select('is_staff')
|
|
.eq('user_id', userId)
|
|
.maybeSingle();
|
|
return !!staffRow?.is_staff;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function slugify(text: string): string {
|
|
return text
|
|
.toLowerCase()
|
|
.normalize('NFD')
|
|
.replace(/[\u0300-\u036f]/g, '') // Supprimer les accents
|
|
.replace(/[^a-z0-9 -]/g, '') // Supprimer les caractères spéciaux
|
|
.replace(/\s+/g, '-') // Remplacer les espaces par des tirets
|
|
.replace(/-+/g, '-') // Éviter les tirets multiples
|
|
.trim();
|
|
}
|
|
|
|
// POST - Upload PDF pour une facture
|
|
export async function POST(req: Request, { params }: { params: { id: string } }) {
|
|
try {
|
|
const supabase = createRouteHandlerClient({ cookies });
|
|
const { data: { session } } = await supabase.auth.getSession();
|
|
if (!session) return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
|
|
|
|
// Vérifier que l'utilisateur est staff
|
|
const isStaff = await isStaffUser(supabase, session.user.id);
|
|
if (!isStaff) {
|
|
return NextResponse.json({ error: 'forbidden', message: 'Staff access required' }, { status: 403 });
|
|
}
|
|
|
|
// Récupérer la facture pour obtenir les infos nécessaires
|
|
const { data: invoice, error: invoiceError } = await supabase
|
|
.from('invoices')
|
|
.select('*')
|
|
.eq('id', params.id)
|
|
.single();
|
|
|
|
if (invoiceError || !invoice) {
|
|
return NextResponse.json({ error: 'invoice_not_found' }, { status: 404 });
|
|
}
|
|
|
|
// Récupérer le nom de l'organisation
|
|
const { data: org, error: orgError } = await supabase
|
|
.from('organizations')
|
|
.select('structure_api')
|
|
.eq('id', invoice.org_id)
|
|
.single();
|
|
|
|
if (orgError || !org) {
|
|
return NextResponse.json({ error: 'organization_not_found' }, { status: 404 });
|
|
}
|
|
|
|
const formData = await req.formData();
|
|
const file = formData.get('pdf') as File;
|
|
|
|
if (!file) {
|
|
return NextResponse.json({ error: 'no_file_provided' }, { status: 400 });
|
|
}
|
|
|
|
if (file.type !== 'application/pdf') {
|
|
return NextResponse.json({ error: 'invalid_file_type', message: 'Only PDF files are allowed' }, { status: 400 });
|
|
}
|
|
|
|
// Générer le chemin S3
|
|
const clientSlug = slugify(org.structure_api);
|
|
const invoiceNumber = invoice.invoice_number || `invoice-${invoice.id.slice(0, 8)}`;
|
|
const s3Key = `invoices/${clientSlug}/${invoiceNumber}.pdf`;
|
|
|
|
// Upload vers S3
|
|
const { S3Client, PutObjectCommand } = await import("@aws-sdk/client-s3");
|
|
const client = new S3Client({
|
|
region: process.env.AWS_REGION || 'eu-west-3'
|
|
});
|
|
|
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
|
|
const uploadCommand = new PutObjectCommand({
|
|
Bucket: (process.env.AWS_S3_BUCKET || 'odentas-docs').trim(),
|
|
Key: s3Key,
|
|
Body: buffer,
|
|
ContentType: 'application/pdf',
|
|
// Retiré Content-Disposition pour permettre l'ouverture dans le navigateur
|
|
});
|
|
|
|
await client.send(uploadCommand);
|
|
|
|
// Mettre à jour la facture avec la clé S3
|
|
const { error: updateError } = await supabase
|
|
.from('invoices')
|
|
.update({
|
|
pdf_s3_key: s3Key,
|
|
updated_at: new Date().toISOString(),
|
|
})
|
|
.eq('id', params.id);
|
|
|
|
if (updateError) {
|
|
console.error('[upload-pdf] Update error:', updateError);
|
|
return NextResponse.json({ error: 'database_update_failed' }, { status: 500 });
|
|
}
|
|
|
|
return NextResponse.json({
|
|
message: 'PDF uploaded successfully',
|
|
s3_key: s3Key
|
|
});
|
|
} catch (error) {
|
|
console.error('[upload-pdf] Error:', error);
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
return NextResponse.json({ error: 'internal_server_error', message }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
// DELETE - Supprimer le PDF d'une facture
|
|
export async function DELETE(req: Request, { params }: { params: { id: string } }) {
|
|
try {
|
|
const supabase = createRouteHandlerClient({ cookies });
|
|
const { data: { session } } = await supabase.auth.getSession();
|
|
if (!session) return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
|
|
|
|
// Vérifier que l'utilisateur est staff
|
|
const isStaff = await isStaffUser(supabase, session.user.id);
|
|
if (!isStaff) {
|
|
return NextResponse.json({ error: 'forbidden', message: 'Staff access required' }, { status: 403 });
|
|
}
|
|
|
|
// Récupérer la facture pour obtenir la clé S3
|
|
const { data: invoice, error: invoiceError } = await supabase
|
|
.from('invoices')
|
|
.select('pdf_s3_key')
|
|
.eq('id', params.id)
|
|
.single();
|
|
|
|
if (invoiceError || !invoice) {
|
|
return NextResponse.json({ error: 'invoice_not_found' }, { status: 404 });
|
|
}
|
|
|
|
// Supprimer de S3 si un PDF existe
|
|
if (invoice.pdf_s3_key) {
|
|
try {
|
|
const { S3Client, DeleteObjectCommand } = await import("@aws-sdk/client-s3");
|
|
const client = new S3Client({
|
|
region: process.env.AWS_REGION || 'eu-west-3'
|
|
});
|
|
|
|
const deleteCommand = new DeleteObjectCommand({
|
|
Bucket: (process.env.AWS_S3_BUCKET || 'odentas-docs').trim(),
|
|
Key: invoice.pdf_s3_key,
|
|
});
|
|
|
|
await client.send(deleteCommand);
|
|
} catch (s3Error) {
|
|
console.error('[delete-pdf] S3 delete error:', s3Error);
|
|
// Continue même si la suppression S3 échoue
|
|
}
|
|
}
|
|
|
|
// Mettre à jour la facture pour supprimer la référence au PDF
|
|
const { error: updateError } = await supabase
|
|
.from('invoices')
|
|
.update({
|
|
pdf_s3_key: null,
|
|
updated_at: new Date().toISOString(),
|
|
})
|
|
.eq('id', params.id);
|
|
|
|
if (updateError) {
|
|
console.error('[delete-pdf] Update error:', updateError);
|
|
return NextResponse.json({ error: 'database_update_failed' }, { status: 500 });
|
|
}
|
|
|
|
return NextResponse.json({
|
|
message: 'PDF deleted successfully'
|
|
});
|
|
} catch (error) {
|
|
console.error('[delete-pdf] Error:', error);
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
return NextResponse.json({ error: 'internal_server_error', message }, { status: 500 });
|
|
}
|
|
} |