obsidian-vault/ligbox-ops-platform/frontend/assets/email-migration.js
2026-06-19 17:26:42 +00:00

97 lines
4 KiB
JavaScript

/**
* Email Migration UI — Spec 019
*/
const DeskEmailMigration = (() => {
const API = '/api';
function esc(s) {
return String(s ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
async function api(path, options = {}) {
const res = await fetch(`${API}${path}`, {
...options,
headers: { ...authHeaders(), 'Content-Type': 'application/json', ...(options.headers || {}) },
});
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).detail || res.statusText);
return res.json();
}
function gateClass(g) {
if (g === 'ready_for_dns') return 'migration-gate-ready';
if (g === 'warning') return 'migration-gate-warning';
return 'migration-gate-blocked';
}
function jobRow(j) {
return `
<div class="migration-job-row" data-job-id="${j.id}">
<div>
<strong>${esc(j.domain)}</strong>
<div class="ticket-meta">${esc(j.phase)} · gate <span class="${gateClass(j.migration_gate)}">${esc(j.migration_gate)}</span></div>
</div>
<div>
<button type="button" class="btn btn-sm" data-mig-preflight="${j.id}">Preflight</button>
<button type="button" class="btn btn-sm" data-mig-sync="${j.id}">Sync</button>
<button type="button" class="btn btn-sm" data-mig-verify="${j.id}">Verify</button>
<button type="button" class="btn btn-sm btn-primary" data-mig-approve="${j.id}">Aprovar gate</button>
</div>
</div>`;
}
async function renderPage() {
const el = document.getElementById('email-migration-content');
if (!el) return;
el.innerHTML = '<p class="loading">Carregando migrações…</p>';
try {
const data = await api('/v1/migration/jobs');
const jobs = data.jobs || [];
el.innerHTML = `
<div class="card">
<div class="card-head-row">
<h3>Migração E-mail (Spec 019)</h3>
<button type="button" class="btn btn-primary btn-sm" id="mig-new-job">+ Novo job</button>
</div>
<p class="ticket-meta">Legado → Carbonio VM112 · Gate DNS antes de MX</p>
${jobs.length ? jobs.map(jobRow).join('') : '<p class="loading">Nenhum job — crie um para iniciar</p>'}
</div>`;
el.querySelector('#mig-new-job')?.addEventListener('click', async () => {
const domain = prompt('Domínio a migrar:');
if (!domain) return;
const email = prompt('Mailbox principal (email):', `admin@${domain}`);
await api('/v1/migration/jobs', {
method: 'POST',
body: JSON.stringify({
domain,
dest_imap_host: `mail.${domain}`,
mailboxes: [{ email: email || `admin@${domain}`, source_host: prompt('IMAP origem (host):') || '' }],
}),
});
await renderPage();
});
el.querySelectorAll('[data-mig-preflight]').forEach((b) => b.addEventListener('click', async () => {
await api(`/v1/migration/jobs/${b.dataset.migPreflight}/preflight`, { method: 'POST' });
await renderPage();
}));
el.querySelectorAll('[data-mig-sync]').forEach((b) => b.addEventListener('click', async () => {
await api(`/v1/migration/jobs/${b.dataset.migSync}/sync?run_type=initial`, { method: 'POST' });
await renderPage();
}));
el.querySelectorAll('[data-mig-verify]').forEach((b) => b.addEventListener('click', async () => {
const r = await api(`/v1/migration/jobs/${b.dataset.migVerify}/verify`);
alert(`Verify: ${r.avg_sync_percent}% · gate ${r.gate}`);
await renderPage();
}));
el.querySelectorAll('[data-mig-approve]').forEach((b) => b.addEventListener('click', async () => {
await api(`/v1/migration/jobs/${b.dataset.migApprove}/approve-gate`, { method: 'POST', body: '{}' });
await renderPage();
}));
} catch (e) {
el.innerHTML = `<p class="loading">Erro: ${esc(e.message)}</p>`;
}
}
return { renderPage };
})();
window.DeskEmailMigration = DeskEmailMigration;