Checkpoint: Historique des imports enrichi avec le compte, la source, le déclencheur, les résultats, les erreurs et les informations du fichier source. Les administrateurs peuvent déclencher une lecture e-mail ponctuelle pour une boîte configurée, avec confirmation explicite, sans activer ni créer de planification. Migration importTrigger appliquée en sandbox ; TypeScript, 48 tests, build et rendus bureau/mobile validés.
This commit is contained in:
58
server/db.ts
58
server/db.ts
@@ -1,4 +1,4 @@
|
||||
import { eq, and, desc, sql, inArray } from "drizzle-orm";
|
||||
import { eq, and, desc, sql, inArray, getTableColumns } from "drizzle-orm";
|
||||
import { drizzle } from "drizzle-orm/mysql2";
|
||||
import {
|
||||
InsertUser,
|
||||
@@ -538,6 +538,44 @@ export async function getAllImportLogs(): Promise<ImportLog[]> {
|
||||
return db.select().from(importLogs).orderBy(desc(importLogs.importedAt));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne les informations nécessaires à l'audit d'un import sans exposer
|
||||
* les paramètres sensibles de messagerie. L'administrateur peut ainsi relier
|
||||
* chaque import à son compte, fichier source et déclencheur.
|
||||
*/
|
||||
export async function getImportLogsWithDetailsByUser(userId: number) {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
return db.select({
|
||||
...getTableColumns(importLogs),
|
||||
userName: users.name,
|
||||
userEmail: users.email,
|
||||
sourceCreatedAt: sourceFiles.createdAt,
|
||||
sourceStatus: sourceFiles.processingStatus,
|
||||
})
|
||||
.from(importLogs)
|
||||
.leftJoin(users, eq(importLogs.userId, users.id))
|
||||
.leftJoin(sourceFiles, eq(importLogs.sourceFileId, sourceFiles.id))
|
||||
.where(eq(importLogs.userId, userId))
|
||||
.orderBy(desc(importLogs.importedAt));
|
||||
}
|
||||
|
||||
export async function getAllImportLogsWithDetails() {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
return db.select({
|
||||
...getTableColumns(importLogs),
|
||||
userName: users.name,
|
||||
userEmail: users.email,
|
||||
sourceCreatedAt: sourceFiles.createdAt,
|
||||
sourceStatus: sourceFiles.processingStatus,
|
||||
})
|
||||
.from(importLogs)
|
||||
.leftJoin(users, eq(importLogs.userId, users.id))
|
||||
.leftJoin(sourceFiles, eq(importLogs.sourceFileId, sourceFiles.id))
|
||||
.orderBy(desc(importLogs.importedAt));
|
||||
}
|
||||
|
||||
export async function deleteAllImportLogs(userId: number | null): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
@@ -611,6 +649,24 @@ export async function upsertImportSettings(data: InsertImportSettings): Promise<
|
||||
}
|
||||
}
|
||||
|
||||
/** Liste administrative des comptes e-mail configurés, sans secret IMAP ni Azure AD. */
|
||||
export async function getEmailImportAccounts() {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
return db.select({
|
||||
userId: importSettings.userId,
|
||||
userName: users.name,
|
||||
userEmail: users.email,
|
||||
emailAddress: importSettings.emailImportAddress,
|
||||
authMode: importSettings.emailImportAuthMode,
|
||||
automaticEnabled: importSettings.emailImportEnabled,
|
||||
isConfigured: sql<number>`CASE WHEN ${importSettings.emailImportAddress} IS NOT NULL AND ${importSettings.emailImportAddress} <> '' AND ${importSettings.emailImportHost} IS NOT NULL AND ${importSettings.emailImportHost} <> '' THEN 1 ELSE 0 END`.as("isConfigured"),
|
||||
})
|
||||
.from(importSettings)
|
||||
.innerJoin(users, eq(importSettings.userId, users.id))
|
||||
.orderBy(users.name, users.email);
|
||||
}
|
||||
|
||||
// ============= DEPARTMENT LIST OPERATIONS =============
|
||||
|
||||
export async function getDepartmentsByUser(_userId?: number): Promise<Department[]> {
|
||||
|
||||
Reference in New Issue
Block a user