espace-paie-odentas/app/api/staff/facturation/[id]/upload-pdf/route.ts
2025-10-12 17:05:46 +02:00

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 });
}
}