feat: warningMessage quota IA dans rapports d'import + redémarrage auto services
This commit is contained in:
@@ -25,6 +25,7 @@ type ImportLog = {
|
||||
errors: number;
|
||||
duplicateDetails: string | null;
|
||||
errorDetails: string | null;
|
||||
warningMessage?: string | null;
|
||||
importedAt: Date | string;
|
||||
};
|
||||
|
||||
@@ -390,7 +391,7 @@ export default function ImportReport() {
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
{(log.duplicatesIgnored > 0 || log.errors > 0) && (
|
||||
{(log.duplicatesIgnored > 0 || log.errors > 0 || (log as any).warningMessage) && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
@@ -486,6 +487,17 @@ export default function ImportReport() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Avertissement quota IA */}
|
||||
{selectedLog.warningMessage && (
|
||||
<div className="bg-orange-50 border border-orange-300 rounded-lg p-4 flex gap-3">
|
||||
<AlertTriangle className="w-5 h-5 text-orange-500 flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-orange-700 mb-1">Avertissement</p>
|
||||
<p className="text-sm text-orange-600">{selectedLog.warningMessage}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Erreurs */}
|
||||
{selectedLog.errors > 0 && (
|
||||
<div>
|
||||
|
||||
1
drizzle/0031_mean_squadron_supreme.sql
Normal file
1
drizzle/0031_mean_squadron_supreme.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE `importLogs` ADD `warningMessage` text;
|
||||
2147
drizzle/meta/0031_snapshot.json
Normal file
2147
drizzle/meta/0031_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -218,6 +218,13 @@
|
||||
"when": 1780930222253,
|
||||
"tag": "0030_vengeful_warbird",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 31,
|
||||
"version": "5",
|
||||
"when": 1781021650065,
|
||||
"tag": "0031_mean_squadron_supreme",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -222,6 +222,7 @@ export const importLogs = mysqlTable("importLogs", {
|
||||
errors: int("errors").default(0).notNull(),
|
||||
duplicateDetails: text("duplicateDetails"), // JSON array of duplicate invoice info
|
||||
errorDetails: text("errorDetails"), // JSON array of error messages
|
||||
warningMessage: text("warningMessage"), // Warning message (e.g. quota exhausted)
|
||||
importedAt: timestamp("importedAt").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ import { registerOAuthRoutes } from "./oauth";
|
||||
import { appRouter } from "../routers";
|
||||
import { createContext } from "./context";
|
||||
import { serveStatic, setupVite } from "./vite";
|
||||
import { getAllUsers } from "../db";
|
||||
import { startEmailImportService } from "../emailImportService";
|
||||
import { startFolderImportService } from "../folderImportService";
|
||||
import { getImportSettingsByUser } from "../db";
|
||||
|
||||
function isPortAvailable(port: number): Promise<boolean> {
|
||||
return new Promise(resolve => {
|
||||
@@ -164,6 +168,31 @@ async function startServer() {
|
||||
server.listen(port, () => {
|
||||
console.log(`Server running on http://localhost:${port}/`);
|
||||
});
|
||||
|
||||
// Redémarrer automatiquement les services actifs (IMAP, dossier) après redémarrage du serveur
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const users = await getAllUsers();
|
||||
for (const user of users) {
|
||||
const settings = await getImportSettingsByUser(user.id);
|
||||
if (!settings) continue;
|
||||
if (settings.emailImportEnabled === 1) {
|
||||
console.log(`[AutoRestart] Restarting email import service for user ${user.id}...`);
|
||||
await startEmailImportService(user.id).catch(e =>
|
||||
console.error(`[AutoRestart] Failed to restart email service for user ${user.id}:`, e.message)
|
||||
);
|
||||
}
|
||||
if ((settings as any).autoImportEnabled === 1) {
|
||||
console.log(`[AutoRestart] Restarting folder import service for user ${user.id}...`);
|
||||
await startFolderImportService(user.id).catch(e =>
|
||||
console.error(`[AutoRestart] Failed to restart folder service for user ${user.id}:`, e.message)
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error('[AutoRestart] Error during service auto-restart:', e.message);
|
||||
}
|
||||
}, 5000); // Attendre 5s que le serveur soit prêt
|
||||
}
|
||||
|
||||
startServer().catch(console.error);
|
||||
|
||||
@@ -40,7 +40,7 @@ async function processEmailAttachment(
|
||||
userId: number,
|
||||
attachment: Attachment,
|
||||
emailSubject: string
|
||||
): Promise<{ success: boolean; totalInvoices: number; imported: number; duplicates: number; errors: number }> {
|
||||
): Promise<{ success: boolean; totalInvoices: number; imported: number; duplicates: number; errors: number; quotaError?: boolean }> {
|
||||
const fileName = attachment.filename || `email-attachment-${Date.now()}.pdf`;
|
||||
console.log(`[EmailImport] Processing attachment: ${fileName} from email: ${emailSubject}`);
|
||||
|
||||
@@ -206,6 +206,20 @@ async function processEmailAttachment(
|
||||
processingProgress: `Terminé: ${importedCount} importée(s), ${duplicatesCount} doublon(s)`,
|
||||
});
|
||||
|
||||
// Détecter si des erreurs de quota ont eu lieu
|
||||
const quotaErrors = errorDetails.filter(e =>
|
||||
e.error && (
|
||||
e.error.includes('usage exhausted') ||
|
||||
e.error.includes('quota') ||
|
||||
e.error.includes('rate limit') ||
|
||||
e.error.includes('Precondition Failed') ||
|
||||
e.error.includes('429')
|
||||
)
|
||||
);
|
||||
const warningMessage = quotaErrors.length > 0
|
||||
? `Quota IA épuisé : ${quotaErrors.length} facture(s) non extraite(s). Vérifiez votre quota Manus ou configurez une clé API externe dans Paramétrage → IA.`
|
||||
: null;
|
||||
|
||||
// Create import log
|
||||
await createImportLog({
|
||||
userId,
|
||||
@@ -217,6 +231,7 @@ async function processEmailAttachment(
|
||||
errors: errorsCount,
|
||||
duplicateDetails: duplicateDetails.length > 0 ? JSON.stringify(duplicateDetails) : null,
|
||||
errorDetails: errorDetails.length > 0 ? JSON.stringify(errorDetails) : null,
|
||||
warningMessage,
|
||||
});
|
||||
|
||||
console.log(`[EmailImport] Successfully processed attachment: ${fileName}`);
|
||||
@@ -229,14 +244,23 @@ async function processEmailAttachment(
|
||||
duplicates: duplicatesCount,
|
||||
errors: errorsCount,
|
||||
};
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error(`[EmailImport] Error processing attachment ${attachment.filename}:`, error);
|
||||
// Détecter l'erreur de quota IA
|
||||
const isQuotaError = error?.message && (
|
||||
error.message.includes('usage exhausted') ||
|
||||
error.message.includes('quota') ||
|
||||
error.message.includes('429') ||
|
||||
error.message.includes('rate limit') ||
|
||||
error.message.includes('Precondition Failed')
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
totalInvoices: 0,
|
||||
imported: 0,
|
||||
duplicates: 0,
|
||||
errors: 1,
|
||||
quotaError: isQuotaError as boolean,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user