feat: améliorations SharePoint - test connexion Azure AD, alerte expiration secret, log statut upload dans BapHistory

This commit is contained in:
Manus
2026-05-06 07:55:46 -04:00
parent bbee4cd7c9
commit 6553aee8a6
5 changed files with 176 additions and 1 deletions

View File

@@ -476,6 +476,9 @@ export const appRouter = router({
let pdfUrl: string | null = null;
let exportPath: string | null = null;
let signatureName: string | null = null;
let sharepointUploadStatus: 'success' | 'error' | 'skipped' | null = null;
let sharepointUploadPath: string | null = null;
let sharepointUploadError: string | null = null;
try {
if (!invoice.fileKey && !invoice.fileUrl) throw new Error('Fichier PDF source introuvable');
@@ -567,8 +570,12 @@ export const appRouter = router({
);
if (spResult.success) {
exportPath = spResult.webUrl || exportFolder;
sharepointUploadStatus = 'success';
sharepointUploadPath = spResult.webUrl || exportFolder;
} else {
console.warn('[BAP] Erreur upload SharePoint:', spResult.error);
sharepointUploadStatus = 'error';
sharepointUploadError = spResult.error || 'Erreur inconnue';
// Fallback : stocker localement
const bapKey = generateStorageKey(ctx.user.id, bapFilename);
const { url } = await localStoragePut(bapKey, Buffer.from(signedPdfBytes), 'application/pdf');
@@ -615,6 +622,9 @@ export const appRouter = router({
exportPath: exportPath || null,
pdfUrl: pdfUrl || null,
signatureName: signatureName || null,
sharepointUploadStatus: (sharepointUploadStatus as any) || null,
sharepointUploadPath: sharepointUploadPath || null,
sharepointUploadError: sharepointUploadError || null,
validatedAt,
});
@@ -662,6 +672,9 @@ export const appRouter = router({
let pdfUrl: string | null = null;
let exportPath: string | null = null;
let signatureName: string | null = null;
let sharepointUploadStatus: 'success' | 'error' | 'skipped' | null = null;
let sharepointUploadPath: string | null = null;
let sharepointUploadError: string | null = null;
try {
// ─ Lecture du PDF source ─
let sourcePdfBytes: Buffer;
@@ -782,6 +795,9 @@ export const appRouter = router({
exportPath: exportPath || null,
pdfUrl: pdfUrl || null,
signatureName: signatureName || null,
sharepointUploadStatus: (sharepointUploadStatus as any) || null,
sharepointUploadPath: sharepointUploadPath || null,
sharepointUploadError: sharepointUploadError || null,
validatedAt,
});
results.push({ id: invoice.id, success: true, pdfUrl, exportPath });
@@ -1489,6 +1505,7 @@ export const appRouter = router({
azureTenantId: z.string().nullable().optional(),
azureClientId: z.string().nullable().optional(),
azureClientSecret: z.string().nullable().optional(),
azureSecretExpiresAt: z.date().nullable().optional(),
}))
.mutation(async ({ input, ctx }) => {
const settings = await upsertImportSettings({
@@ -1498,6 +1515,57 @@ export const appRouter = router({
return settings;
}),
testAzureConnection: protectedProcedure
.mutation(async ({ ctx }) => {
const settings = await getImportSettingsByUser(ctx.user.id);
const tenantId = (settings as any)?.azureTenantId;
const clientId = (settings as any)?.azureClientId;
const clientSecret = (settings as any)?.azureClientSecret;
const sharepointUrl = settings?.exportFolder;
if (!tenantId || !clientId || !clientSecret) {
return { success: false, error: 'Credentials Azure AD manquants (Tenant ID, Client ID ou Secret)' };
}
try {
const { uploadToSharePoint } = await import('./sharepoint');
// Test : obtenir un token OAuth2 uniquement (sans upload)
const tokenRes = await fetch(
`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
scope: 'https://graph.microsoft.com/.default',
}),
}
);
const tokenData = await tokenRes.json() as any;
if (tokenData.error) {
return { success: false, error: `Erreur Azure AD : ${tokenData.error_description || tokenData.error}` };
}
// Test accès SharePoint si URL configurée
if (sharepointUrl) {
const urlObj = new URL(sharepointUrl);
const hostname = urlObj.hostname; // ex: itinova.sharepoint.com
const siteTestRes = await fetch(
`https://graph.microsoft.com/v1.0/sites/${hostname}`,
{ headers: { Authorization: `Bearer ${tokenData.access_token}` } }
);
if (!siteTestRes.ok) {
const siteErr = await siteTestRes.json() as any;
return { success: false, error: `Token OK mais accès SharePoint refusé : ${siteErr?.error?.message || siteTestRes.status}` };
}
}
return { success: true, message: 'Connexion Azure AD réussie' + (sharepointUrl ? ' et accès SharePoint vérifié' : '') };
} catch (err: any) {
return { success: false, error: err.message || 'Erreur inconnue' };
}
}),
}),
// ============= DEPARTMENT LIST ROUTES =============