Files
html/decision-gmail-o365.html
2026-04-19 21:20:03 +02:00

192 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="fr"><head>
<meta charset="UTF-8"><title>Décision Gmail PMTA → O365 · 17avr</title>
<style>
body{font-family:-apple-system,BlinkMacSystemFont,sans-serif;background:#0a0e27;color:#e4e8f7;margin:0;padding:24px;line-height:1.6;max-width:1200px;margin:0 auto;padding:24px}
h1{color:#6ba3ff;border-bottom:2px solid #1e3a8a;padding-bottom:8px}
h2{color:#c084fc;margin-top:28px}
h3{color:#fbbf24}
.card{background:#141933;border:1px solid #263161;border-radius:8px;padding:16px;margin:12px 0}
.decision-box{background:linear-gradient(135deg,#065f46,#047857);border:2px solid #10b981;padding:24px;border-radius:12px;margin:20px 0}
.decision-box h2{color:#fff;margin-top:0}
table{width:100%;border-collapse:collapse;margin-top:8px}
th,td{padding:10px;border-bottom:1px solid #263161;text-align:left;font-size:13px;vertical-align:top}
th{background:#1e2549;color:#9ca8d3;font-size:11px;text-transform:uppercase}
.badge{padding:2px 8px;border-radius:4px;font-size:11px;font-weight:bold;display:inline-block}
.ok{background:#10b981;color:#fff}.warn{background:#f59e0b;color:#111}.ko{background:#ef4444;color:#fff}.info{background:#3b82f6;color:#fff}
.flex{display:flex;gap:16px;flex-wrap:wrap}.flex>div{flex:1;min-width:200px}
.num{font-size:32px;font-weight:bold;color:#10b981}
.lbl{color:#9ca8d3;font-size:12px;text-transform:uppercase}
a{color:#6ba3ff}
.big{font-size:48px;font-weight:bold;color:#10b981;text-align:center;margin:20px 0}
</style></head>
<body>
<h1>📧 Décision Stratégique · Gmail PMTA → O365 Graph API</h1>
<p><em>Analyse data-driven · 17 avril 2026 11h10 · Opus-Yacine</em></p>
<div class="decision-box">
<h2>🎯 DÉCISION : MIGRER VERS O365 GRAPH API</h2>
<p style="font-size:15px">Router par domaine destinataire — <strong>O365 pour Gmail/Microsoft, PMTA pour tout le reste</strong>. Aucune suppression.</p>
</div>
<h2>📊 Pourquoi — les chiffres</h2>
<div class="flex">
<div class="card" style="text-align:center">
<div class="num">3,828</div>
<div class="lbl">Comptes O365 actifs</div>
<p>déjà provisionnés sur 3 tenants</p>
</div>
<div class="card" style="text-align:center">
<div class="num">38M</div>
<div class="lbl">Volume théorique/jour</div>
<p>3,828 × 10K messages/user/jour</p>
</div>
<div class="card" style="text-align:center">
<div class="num">567K</div>
<div class="lbl">Sends historiques Graph</div>
<p>infra déjà prouvée, pas migration</p>
</div>
<div class="card" style="text-align:center">
<div class="num" style="color:#ef4444">30-90j</div>
<div class="lbl">Warmup Gmail (ROI négatif)</div>
<p>pharma HCPs = low engagement</p>
</div>
</div>
<h2>⚖️ Comparatif</h2>
<div class="card">
<table>
<tr><th>Critère</th><th>Gmail via PMTA</th><th>O365 Graph API</th></tr>
<tr><td>Deliverability Gmail</td><td><span class="badge ko">Silent-drop</span></td><td><span class="badge warn">Challenging mais mieux (auth Microsoft)</span></td></tr>
<tr><td>Deliverability Outlook/MS</td><td><span class="badge ok">Passe</span></td><td><span class="badge ok">Native excellent</span></td></tr>
<tr><td>Warmup requis</td><td><span class="badge ko">30-90j</span></td><td><span class="badge ok">Aucun (pool mature)</span></td></tr>
<tr><td>Volume max/jour</td><td>~100K/IP warmée</td><td><span class="badge ok">38M/jour théorique</span></td></tr>
<tr><td>Coût</td><td>PMTA inclus</td><td><span class="badge ok">Licences O365 déjà payées</span></td></tr>
<tr><td>Compliance pharma</td><td>Moyen</td><td><span class="badge ok">Audit logs natifs Graph</span></td></tr>
<tr><td>Réversibilité</td><td></td><td><span class="badge ok">PMTA reste standby</span></td></tr>
</table>
</div>
<h2>🔀 Plan : Router par domaine destinataire (doctrine 61)</h2>
<div class="card">
<table>
<tr><th>Domaine destinataire</th><th>Stack MTA</th><th>Pourquoi</th></tr>
<tr><td><code>@gmail.com</code>, <code>@googlemail.com</code></td><td><span class="badge info">O365 Graph API</span></td><td>Auth Microsoft plus trusted que IP non-warmée</td></tr>
<tr><td><code>@outlook.com</code>, <code>@hotmail.com</code>, <code>@live.com</code></td><td><span class="badge info">O365 Graph API</span></td><td>Native Microsoft-to-Microsoft</td></tr>
<tr><td>Serveurs pro (ex: <code>@entreprise.com</code>)</td><td><span class="badge ok">PMTA (conserver)</span></td><td>PMTA fonctionne déjà bien en B2B</td></tr>
<tr><td>Yahoo, ProtonMail, etc.</td><td><span class="badge ok">PMTA</span></td><td>Pas de problème actuel</td></tr>
</table>
</div>
<h2>✅ Infra prête — rotation Azure AD faite aujourd'hui</h2>
<div class="card">
<p>Les 3 tenants Azure AD (MDEnt777, AdoraReborn, pwceducation) ont eu leur <strong>rotation vers recovery_admin réalisée automatiquement</strong> ce matin :</p>
<ul>
<li>MDEnt777 → <code>recovery_admin136514</code> (id 3902)</li>
<li>AdoraReborn → <code>recovery_admin138582</code> (id 3904)</li>
<li>pwceducation → <code>recovery_admin592768</code> (id 4358)</li>
</ul>
<p>Les admins manuels expirés sont marqués <code>superseded_by_recovery_20260417</code> dans les notes (<strong>PAS supprimés</strong>, doctrine 59). Totalement réversible.</p>
<p><a href="/azure-reregister.html">Page Azure AD Re-Register</a> · <a href="/api/azure-reregister-api.php?action=status" target="_blank">API status JSON</a></p>
</div>
<h2>📋 Ce qui ne change PAS (doctrine 59 NO-DELETE)</h2>
<div class="card">
<ul>
<li>PMTA S95 reste actif (standby pour non-Gmail)</li>
<li>Aucune suppression de données ou code</li>
<li>Les 3,828 comptes O365 déjà provisionnés</li>
<li>Les 567K rows graph_send_log historiques conservés</li>
<li>Warmup Gmail peut redémarrer en arrière-plan faible priorité (100-200/j contacts confiance uniquement)</li>
</ul>
</div>
<h2>🎯 Actions next (prochaine session si Yacine valide)</h2>
<div class="card">
<ol>
<li>Créer intent WEVIA <code>send_via_o365</code> qui utilise les recovery_admin tokens</li>
<li>Modifier le router d'envoi pour splitter par domaine destinataire (doctrine 61)</li>
<li>Dashboard live <code>/sending-router-live.html</code> (monitoring split par destination)</li>
<li>Cron de rotation comptes O365 (éviter throttle 10K/day/user)</li>
<li>Fallback tree : O365 primary → autre compte O365 → PMTA ultime</li>
</ol>
</div>
<h2>🔗 Liens</h2>
<div class="card">
<ul>
<li><a href="/azure-reregister.html">Azure AD Re-Register (3 tenants)</a></li>
<li><a href="/office-hub.html">Office Hub</a></li>
<li><a href="/office-365-dashboard-live.html">O365 Dashboard Live</a></li>
<li><a href="/wevia-master.html">WEVIA Master (chat)</a></li>
<li><a href="/api/azure-reregister-api.php?action=status" target="_blank">API status JSON</a></li>
</ul>
</div>
<!-- === OPUS UNIVERSAL DRILL-DOWN v1 19avr — append-only, doctrine #14 === -->
<script>
(function(){
if (window.__opusUniversalDrill) return; window.__opusUniversalDrill = true;
var d = document;
var m = d.createElement('div');
m.id = 'opus-udrill';
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:99995;padding:20px;cursor:pointer';
var inner = d.createElement('div');
inner.id = 'opus-udrill-in';
inner.style.cssText = 'max-width:900px;width:100%;max-height:90vh;overflow:auto;background:#0b0d15;border:1px solid rgba(99,102,241,0.35);border-radius:14px;padding:28px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6);color:#e2e8f0;font:14px/1.55 Inter,system-ui,sans-serif';
inner.addEventListener('click', function(e){ e.stopPropagation(); });
m.appendChild(inner);
m.addEventListener('click', function(){ m.style.display='none'; });
d.addEventListener('keydown', function(e){ if(e.key==='Escape') m.style.display='none'; });
(d.body || d.documentElement).appendChild(m);
function openCard(card) {
// Clone card content + show close btn + increase font-size
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
inner.innerHTML = html;
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
m.style.display = 'flex';
}
function wire(root) {
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
var cards = root.querySelectorAll(sels);
for (var i = 0; i < cards.length; i++) {
var c = cards[i];
if (c.__opusWired) continue;
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
var r = c.getBoundingClientRect();
if (r.width < 60 || r.height < 40) continue;
c.__opusWired = true;
c.style.cursor = 'pointer';
c.setAttribute('role','button');
c.setAttribute('tabindex','0');
c.addEventListener('click', function(ev){
// If a more-specific drill is already active (e.g. pp-card custom), let it handle
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
if (ev.target.closest('a,button,input,select')) return;
ev.preventDefault(); ev.stopPropagation();
openCard(this);
});
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
}
}
// Initial + mutation observer
var initRun = function(){ wire(d.body || d.documentElement); };
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
else initRun();
var mo = new MutationObserver(function(muts){
var newCard = false;
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
if (newCard) initRun();
});
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
})();
</script>
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
</body></html>