← Volver al índice
✉ Exchange Online — Recuperación de correo

Mail Recover

Herramienta de recuperación de correos eliminados desde Recoverable Items de Exchange Online. Búsqueda, restauración individual y masiva con progreso en tiempo real.

🌟 Python 3 + Flask + Waitress
Graph API + Exchange PS
🌐 Puerto 5000
🏠 WinSW Service
En producción
3
Carpetas Recoverable Items
4
Lote máximo batch (Exchange)
7d
Retención historial
SSE
Progreso en tiempo real

Cómo funciona

Búsqueda y restauración asíncronas mediante worker threads. El browser recibe actualizaciones en tiempo real via Server-Sent Events con soporte de reconexión y replay completo.

🔍 Búsqueda

Chunking por día para evitar $skip profundo. Busca en recoverableitemsdeletions, versions y purges. Resultados persistidos en SQLite 7 días.

↻ Restauración individual

Selección individual o "Restaurar todos". Graph $batch API en lotes de 4 (límite MailboxConcurrency Exchange). Destino: carpeta "Restore" del buzón.

⏷ Recuperación masiva

Sin búsqueda previa. Cuenta ítems primero ($count Graph, ~2s). Restaura por lotes de 1 página volviendo a skip=0 — evita 504 en buzón grandes.

Búsqueda y restauración

El admin introduce email y rango de fechas. La búsqueda corre en background con progreso SSE. La restauración también es asíncrona con barra de progreso en tiempo real.

1
Admin envía formulario (email, date_from, date_to, max_results) → POST /search
2
search_worker thread: chunking por día → search_folder() en las 3 carpetas Recoverable Items → ordena por receivedDateTime DESC
3
Browser recibe progreso via SSE (EventSource /search/stream?token=...). Si se desconecta, replay completo desde el historial del job.
4
Resultados en tabla con selección individual o "Restaurar todos" → POST /restore
5
restore_worker: Graph /$batch en lotes de 4 → mueve mensajes a carpeta "Restore" del buzón del usuario
6
Informe HTML descargable con estado por ítem. Opción de reintentar fallidos.

Estadísticas de retención

Informe visual de uso de Recoverable Items por política de retención. Datos via PowerShell Exchange Online — Get-Mailbox + Get-MailboxFolderStatistics en streaming.

📊 Grupos de retención

retention6Política estándar 6 años
retention10Equipo legal (10 años)
retention0Proceso judicial activo (sin borrado)

📰 Informe generado

Por grupo: buzón, n.º ítems, tamaño total, política MRM, barra de distribución. Exportable a HTML (para Purview) y CSV. Streaming SSE — sin esperar a que termine todo para ver resultados.

Componentes principales

MóduloResponsabilidad
graph.pyCliente Graph API: paginación automática, retry en 429/503/504, batch API. graph_get(), graph_count(), graph_batch_post()
recovery.pyLógica de búsqueda y restauración. Chunking por día, get_or_create_restore_folder(), restore_items_batch(), stream_restore_to_folder()
exchange_ps.pyWrapper PowerShell Exchange Online. stream_mailboxes_with_stats() con auth certificado CurrentUser\My
search_db.pySQLite historial búsquedas (TTL 7d). create_pending(), mark_done(), get_search(). Thread-safe con mutex.
search_worker.pyThread búsqueda. Acumula mensajes para replay SSE via threading.Condition()
restore_worker.pyThread restauración individual. Estado + resultado final en memoria.
mass_restore_worker.pyThread recuperación masiva. Itera páginas sin acumular $skip. Yield por ítem para CSV.
report.pyInforme HTML restauración: resumen estadístico + tabla por ítem
retention_report.pyInforme HTML retención: barras distribución, badges política MRM, totales

Rutas HTTP

RutaMétodoDescripción
/GETFormulario de búsqueda
/searchPOSTInicia búsqueda (crea worker thread)
/search/streamGETSSE: progreso búsqueda en tiempo real
/results/{token}GETResultados + selección para restaurar
/restorePOSTInicia restauración
/restore/stream/{token}GETSSE: progreso restauración
/restore/done/{token}GETResultado final + descarga informe
/historyGETHistorial búsquedas (7 días)
/mass-restoreGET/POSTRecuperación masiva por rango de fechas
/retention-statsGETEstadísticas retención por grupo
/retention-stats/streamGETSSE: stats por CustomAttribute en tiempo real

Consideraciones clave

⚠ $skip profundo

Graph falla con 504 si $skip > ~10.000 en buzón grandes. Solución: chunking por día en búsqueda. En restauración masiva: lote de 1 página (100 ítems), restaurar, volver a skip=0.

🔓 MailboxConcurrency

Exchange Online limita conexiones simultáneas a un buzón. La restauración usa Graph /$batch en lotes de 4 para no superar el límite.

🔁 SSE con replay

Los workers acumulan mensajes en job["messages"] con threading.Condition(). Si el browser pierde la conexión, al reconectar recibe el historial completo desde el inicio.

💾 Diferencia masiva vs individual

Individual: filtra por receivedDateTime, restaura a carpeta "Restore". Masiva: filtra por lastModifiedDateTime (fecha borrado), restaura a carpeta original.

Deploy y configuración

Deploy

cd C:\apps\jmfernandez\mailrecover .\deploy.ps1 # git pull + pip install sisapps-ui + restart

Config clave (app-config.json)

ms_graph.client_idApp Registration para Graph API
ms_graph.certificate_thumbprintCert en CurrentUser\My
server.portPuerto (default 5000)
search_history.ttl_daysRetención historial (default 7)
sso.dev_bypass_upnBypass SSO en desarrollo
Mail Recover · Equipo de Sistemas · 2026