feat: intégration Microsoft Graph pour export SharePoint - helper uploadToSharePoint, champs Azure AD en DB et UI

This commit is contained in:
Manus
2026-05-06 07:29:23 -04:00
parent 382ac51943
commit 766f2b444d
5 changed files with 353 additions and 16 deletions

View File

@@ -470,6 +470,7 @@ export const appRouter = router({
const importSettings = await getImportSettingsByUser(ctx.user.id);
const bapExportMode = importSettings?.bapExportMode || 'browser';
const exportFolder = importSettings?.exportFolder || null;
const exportFolderType = (importSettings as any)?.exportFolderType || 'local';
const STORAGE_BASE_PATH = process.env.STORAGE_BASE_PATH || path.join(process.cwd(), 'storage');
let pdfUrl: string | null = null;
@@ -550,12 +551,37 @@ export const appRouter = router({
const filename = path.basename(invoice.fileKey);
const bapFilename = `BAP_${Date.now()}_${filename}`;
if (bapExportMode === 'folder' && exportFolder) {
// Mode dossier : enregistrer sur le disque
await fs.mkdir(exportFolder, { recursive: true });
exportPath = path.join(exportFolder, bapFilename);
await fs.writeFile(exportPath, signedPdfBytes);
} else {
if ((bapExportMode === 'folder' || bapExportMode === 'both') && exportFolder) {
if (exportFolderType === 'sharepoint') {
// Mode SharePoint : upload via Microsoft Graph
const { uploadToSharePoint } = await import('./sharepoint');
const spResult = await uploadToSharePoint(
{
tenantId: (importSettings as any)?.azureTenantId || '',
clientId: (importSettings as any)?.azureClientId || '',
clientSecret: (importSettings as any)?.azureClientSecret || '',
sharepointUrl: exportFolder,
},
Buffer.from(signedPdfBytes),
bapFilename
);
if (spResult.success) {
exportPath = spResult.webUrl || exportFolder;
} else {
console.warn('[BAP] Erreur upload SharePoint:', spResult.error);
// Fallback : stocker localement
const bapKey = generateStorageKey(ctx.user.id, bapFilename);
const { url } = await localStoragePut(bapKey, Buffer.from(signedPdfBytes), 'application/pdf');
pdfUrl = url;
}
} else {
// Mode dossier local : enregistrer sur le disque
await fs.mkdir(exportFolder, { recursive: true });
exportPath = path.join(exportFolder, bapFilename);
await fs.writeFile(exportPath, signedPdfBytes);
}
}
if (bapExportMode === 'browser' || bapExportMode === 'both' || !exportFolder) {
// Mode navigateur : stocker localement et retourner l'URL relative
const bapKey = generateStorageKey(ctx.user.id, bapFilename);
const { url } = await localStoragePut(bapKey, Buffer.from(signedPdfBytes), 'application/pdf');
@@ -625,6 +651,7 @@ export const appRouter = router({
const importSettings = await getImportSettingsByUser(ctx.user.id);
const bapExportMode = importSettings?.bapExportMode || 'browser';
const exportFolder = importSettings?.exportFolder || null;
const exportFolderType = (importSettings as any)?.exportFolderType || 'local';
const STORAGE_BASE_PATH = process.env.STORAGE_BASE_PATH || path.join(process.cwd(), 'storage');
const serviceSignaturesList = await getServiceSignaturesByUser(ctx.user.id);
const results: Array<{ id: number; success: boolean; pdfUrl?: string | null; exportPath?: string | null; error?: string }> = [];
@@ -702,11 +729,34 @@ export const appRouter = router({
const signedPdfBytes = await pdfDoc.save();
const filename = path.basename(invoice.fileKey);
const bapFilename = `BAP_${Date.now()}_${filename}`;
if (bapExportMode === 'folder' && exportFolder) {
await fs.mkdir(exportFolder, { recursive: true });
exportPath = path.join(exportFolder, bapFilename);
await fs.writeFile(exportPath, signedPdfBytes);
} else {
if ((bapExportMode === 'folder' || bapExportMode === 'both') && exportFolder) {
if (exportFolderType === 'sharepoint') {
const { uploadToSharePoint } = await import('./sharepoint');
const spResult = await uploadToSharePoint(
{
tenantId: (importSettings as any)?.azureTenantId || '',
clientId: (importSettings as any)?.azureClientId || '',
clientSecret: (importSettings as any)?.azureClientSecret || '',
sharepointUrl: exportFolder,
},
Buffer.from(signedPdfBytes),
bapFilename
);
if (spResult.success) {
exportPath = spResult.webUrl || exportFolder;
} else {
console.warn('[BAP Bulk] Erreur upload SharePoint:', spResult.error);
const bapKey = generateStorageKey(ctx.user.id, bapFilename);
const { url } = await localStoragePut(bapKey, Buffer.from(signedPdfBytes), 'application/pdf');
pdfUrl = url;
}
} else {
await fs.mkdir(exportFolder, { recursive: true });
exportPath = path.join(exportFolder, bapFilename);
await fs.writeFile(exportPath, signedPdfBytes);
}
}
if (bapExportMode === 'browser' || bapExportMode === 'both' || !exportFolder) {
const bapKey = generateStorageKey(ctx.user.id, bapFilename);
const { url } = await localStoragePut(bapKey, Buffer.from(signedPdfBytes), 'application/pdf');
pdfUrl = url;
@@ -1410,7 +1460,11 @@ export const appRouter = router({
emailImportPort: 993,
emailImportFrequency: 30,
exportFolder: null,
exportFolderType: "local" as const,
bapExportMode: "browser" as const,
azureTenantId: null,
azureClientId: null,
azureClientSecret: null,
};
}
@@ -1430,7 +1484,11 @@ export const appRouter = router({
emailImportPort: z.number().min(1).max(65535).optional(),
emailImportFrequency: z.number().min(1).optional(),
exportFolder: z.string().nullable().optional(),
exportFolderType: z.enum(["local", "teams", "sharepoint"]).optional(),
bapExportMode: z.enum(["browser", "folder", "both"]).optional(),
azureTenantId: z.string().nullable().optional(),
azureClientId: z.string().nullable().optional(),
azureClientSecret: z.string().nullable().optional(),
}))
.mutation(async ({ input, ctx }) => {
const settings = await upsertImportSettings({