Specs stay at repo root (cross-VM). Move deploy and code into logical projects with README per domain, updated manifest.yaml, and symlinks at legacy paths for VM122 backward compatibility.
97 lines
4 KiB
JavaScript
97 lines
4 KiB
JavaScript
/**
|
|
* Email Migration UI — Spec 019
|
|
*/
|
|
const DeskEmailMigration = (() => {
|
|
const API = '/api';
|
|
|
|
function esc(s) {
|
|
return String(s ?? '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
}
|
|
|
|
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;
|