Le problème était que l'update du champ contract_pdf_s3_key utilisait le client Supabase normal (avec RLS) au lieu du client admin (service role). Cela empêchait potentiellement la mise à jour du contrat ou l'accès côté client. Changements: - Import de createClient depuis @supabase/supabase-js - Création d'un adminClient avec SUPABASE_SERVICE_ROLE_KEY - Utilisation de adminClient pour l'update au lieu de sb - Ajout de logs pour le debug
199 lines
6.1 KiB
TypeScript
199 lines
6.1 KiB
TypeScript
// app/api/staff/contrats/[id]/upload-signed-pdf/route.ts
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
import { createSbServer } from "@/lib/supabaseServer";
|
|
import { createClient } from "@supabase/supabase-js";
|
|
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
|
|
|
|
const REGION = process.env.AWS_REGION || "eu-west-3";
|
|
const BUCKET = (process.env.AWS_S3_BUCKET || "odentas-docs").trim();
|
|
|
|
const s3Client = new S3Client({
|
|
region: REGION,
|
|
credentials: {
|
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
|
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
|
|
},
|
|
});
|
|
|
|
export async function POST(
|
|
request: NextRequest,
|
|
{ params }: { params: { id: string } }
|
|
) {
|
|
try {
|
|
console.log("📤 [UPLOAD] Début de l'upload manuel, ID reçu:", params.id);
|
|
|
|
const sb = createSbServer();
|
|
|
|
// Vérification de l'authentification
|
|
const { data: { user }, error: authError } = await sb.auth.getUser();
|
|
if (authError || !user) {
|
|
console.log("❌ [UPLOAD] Erreur authentification:", authError);
|
|
return NextResponse.json(
|
|
{ error: "Non autorisé" },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
console.log("✅ [UPLOAD] Utilisateur authentifié:", user.id);
|
|
|
|
// Vérification des droits staff
|
|
const { data: me } = await sb
|
|
.from("staff_users")
|
|
.select("is_staff")
|
|
.eq("user_id", user.id)
|
|
.maybeSingle();
|
|
|
|
if (!me?.is_staff) {
|
|
console.log("❌ [UPLOAD] Utilisateur non-staff");
|
|
return NextResponse.json(
|
|
{ error: "Accès refusé - staff requis" },
|
|
{ status: 403 }
|
|
);
|
|
}
|
|
|
|
console.log("✅ [UPLOAD] Droits staff validés");
|
|
|
|
// Récupération du contrat
|
|
console.log("🔍 [UPLOAD] Recherche du contrat avec ID:", params.id);
|
|
const { data: contract, error: contractError } = await sb
|
|
.from("cddu_contracts")
|
|
.select("id, contract_number, org_id, contract_pdf_s3_key")
|
|
.eq("id", params.id)
|
|
.single();
|
|
|
|
console.log("🔍 [UPLOAD] Résultat de la recherche:", { contract, contractError });
|
|
|
|
if (contractError || !contract) {
|
|
console.log("❌ [UPLOAD] Contrat introuvable. Erreur:", contractError?.message, "Code:", contractError?.code);
|
|
return NextResponse.json(
|
|
{ error: "Contrat introuvable", details: contractError?.message },
|
|
{ status: 404 }
|
|
);
|
|
}
|
|
|
|
console.log("✅ [UPLOAD] Contrat trouvé:", contract.id, contract.contract_number);
|
|
|
|
// Récupération de l'organisation pour obtenir le nom et le slugifier
|
|
let orgApiName = "unknown_org";
|
|
console.log("🔍 [UPLOAD] org_id du contrat:", contract.org_id);
|
|
|
|
if (contract.org_id) {
|
|
const { data: org, error: orgError } = await sb
|
|
.from("organizations")
|
|
.select("name")
|
|
.eq("id", contract.org_id)
|
|
.single();
|
|
|
|
console.log("🔍 [UPLOAD] Organisation trouvée:", { org, orgError });
|
|
|
|
if (org?.name) {
|
|
// Slugifier le nom de l'organisation
|
|
orgApiName = org.name
|
|
.toLowerCase()
|
|
.normalize('NFD')
|
|
.replace(/[\u0300-\u036f]/g, '')
|
|
.replace(/[^a-z0-9]+/g, '-')
|
|
.replace(/^-+|-+$/g, '');
|
|
console.log("✅ [UPLOAD] Nom d'organisation slugifié:", orgApiName);
|
|
} else {
|
|
console.log("❌ [UPLOAD] Organisation sans nom");
|
|
}
|
|
} else {
|
|
console.log("❌ [UPLOAD] Contrat sans org_id");
|
|
}
|
|
|
|
// Récupération du fichier depuis FormData
|
|
const formData = await request.formData();
|
|
const file = formData.get("file") as File;
|
|
|
|
if (!file) {
|
|
return NextResponse.json(
|
|
{ error: "Aucun fichier fourni" },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Vérification du type MIME
|
|
if (file.type !== "application/pdf") {
|
|
return NextResponse.json(
|
|
{ error: "Le fichier doit être un PDF" },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Conversion du fichier en buffer
|
|
const arrayBuffer = await file.arrayBuffer();
|
|
const buffer = Buffer.from(arrayBuffer);
|
|
|
|
// Génération de la clé S3 (même format que Docuseal)
|
|
// Format: contracts/<org-slug>/<contract_number>.pdf
|
|
const s3Key = `contracts/${orgApiName}/${contract.contract_number}.pdf`;
|
|
|
|
console.log("📤 Upload manuel du contrat signé:", {
|
|
contractId: contract.id,
|
|
contractNumber: contract.contract_number,
|
|
s3Key,
|
|
fileSize: buffer.length,
|
|
});
|
|
|
|
// Upload vers S3
|
|
const uploadParams = {
|
|
Bucket: BUCKET,
|
|
Key: s3Key,
|
|
Body: buffer,
|
|
ContentType: "application/pdf",
|
|
ACL: "private" as const,
|
|
};
|
|
|
|
try {
|
|
const command = new PutObjectCommand(uploadParams);
|
|
await s3Client.send(command);
|
|
console.log("✅ PDF uploadé avec succès dans S3:", s3Key);
|
|
} catch (s3Error) {
|
|
console.error("❌ Erreur lors de l'upload S3:", s3Error);
|
|
return NextResponse.json(
|
|
{ error: "Erreur lors de l'upload du fichier" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
// Mise à jour du contrat avec la clé S3 - utiliser le client admin pour bypasser RLS
|
|
console.log("📝 [UPLOAD] Mise à jour du contrat avec le client admin...");
|
|
|
|
const adminClient = createClient(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
|
|
process.env.SUPABASE_SERVICE_ROLE_KEY || ""
|
|
);
|
|
|
|
const { error: updateError } = await adminClient
|
|
.from("cddu_contracts")
|
|
.update({
|
|
contract_pdf_s3_key: s3Key,
|
|
contrat_signe: "Oui", // Marquer le contrat comme signé
|
|
})
|
|
.eq("id", contract.id);
|
|
|
|
if (updateError) {
|
|
console.error("❌ Erreur lors de la mise à jour du contrat:", updateError);
|
|
return NextResponse.json(
|
|
{ error: "Erreur lors de la mise à jour du contrat" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
console.log("✅ Contrat mis à jour avec la clé S3:", contract.id);
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
s3Key,
|
|
message: "Contrat signé uploadé avec succès",
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error("❌ Erreur lors de l'upload du contrat signé:", error);
|
|
return NextResponse.json(
|
|
{ error: "Erreur interne du serveur" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|