feat: améliorations SharePoint - test connexion Azure AD, alerte expiration secret, log statut upload dans BapHistory
This commit is contained in:
@@ -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 =============
|
||||
|
||||
Reference in New Issue
Block a user