Template
Add server user/protocol migrate export-import for connect_domain moves.
This commit is contained in:
@@ -647,6 +647,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== Migrate users / protocol state ===== -->
|
||||
<div class="modal-backdrop" id="migrateModal">
|
||||
<div class="modal" style="max-width: 560px;">
|
||||
<div class="modal-header">
|
||||
<div>
|
||||
<h2 class="modal-title">{{ _('migrate_title') }}</h2>
|
||||
<div class="text-muted text-sm">{{ _('migrate_desc') }}</div>
|
||||
</div>
|
||||
<button class="modal-close" onclick="closeModal('migrateModal')">×</button>
|
||||
</div>
|
||||
<div class="form-hint" style="margin-bottom: var(--space-md);">{{ _('migrate_hint') }}</div>
|
||||
<div class="flex" style="flex-direction: column; gap: var(--space-md);">
|
||||
<div>
|
||||
<div class="form-label">{{ _('migrate_export_label') }}</div>
|
||||
<button type="button" class="btn btn-secondary" id="migrateExportBtn" onclick="exportServerMigrate()"
|
||||
style="width:100%; justify-content:center; gap:var(--space-sm);">
|
||||
⬇️ {{ _('migrate_export_btn') }}
|
||||
</button>
|
||||
</div>
|
||||
<div style="border-top: 1px solid var(--border-color); padding-top: var(--space-md);">
|
||||
<div class="form-label">{{ _('migrate_import_label') }}</div>
|
||||
<input type="file" id="migrateFile" accept=".zip,application/zip" style="display:none;"
|
||||
onchange="importServerMigrate(event)">
|
||||
<button type="button" class="btn btn-primary" id="migrateImportBtn"
|
||||
onclick="document.getElementById('migrateFile').click()"
|
||||
style="width:100%; justify-content:center; gap:var(--space-sm);">
|
||||
⬆️ {{ _('migrate_import_btn') }}
|
||||
</button>
|
||||
<div class="form-hint" style="margin-top: var(--space-xs);">{{ _('migrate_import_hint') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== Install Protocol Modal ===== -->
|
||||
<div class="modal-backdrop" id="installModal">
|
||||
<div class="modal">
|
||||
@@ -1378,6 +1412,9 @@
|
||||
<button class="btn btn-secondary" onclick="closeModal('managementModal'); checkServer();" style="justify-content: flex-start; padding: 12px 20px;">
|
||||
<span style="font-size: 1.2rem; margin-right: 8px; display:inline-flex;">${uiIcon('refresh')}</span> ${_('check_server_services')}
|
||||
</button>
|
||||
<button class="btn btn-primary" onclick="closeModal('managementModal'); openModal('migrateModal');" style="justify-content: flex-start; padding: 12px 20px;">
|
||||
<span style="font-size: 1.2rem; margin-right: 8px;">📦</span> ${_('migrate_title')}
|
||||
</button>
|
||||
<button class="btn btn-warning" onclick="closeModal('managementModal'); rebootServer();" style="justify-content: flex-start; padding: 12px 20px;">
|
||||
<span style="font-size: 1.2rem; margin-right: 8px; display:inline-flex;">${uiIcon('refresh')}</span> ${_('reboot_server')}
|
||||
</button>
|
||||
@@ -1391,6 +1428,79 @@
|
||||
`;
|
||||
}
|
||||
|
||||
async function exportServerMigrate() {
|
||||
const btn = document.getElementById('migrateExportBtn');
|
||||
if (btn) btn.disabled = true;
|
||||
showToast(_('migrate_exporting'), 'info');
|
||||
try {
|
||||
const res = await fetch(`/api/servers/${SERVER_ID}/migrate/export?include_protocols=true`, {
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
if (!res.ok) {
|
||||
let err = _('error');
|
||||
try {
|
||||
const data = await res.json();
|
||||
err = data.error || err;
|
||||
} catch (_) {}
|
||||
throw new Error(err);
|
||||
}
|
||||
const blob = await res.blob();
|
||||
const cd = res.headers.get('Content-Disposition') || '';
|
||||
const match = /filename="?([^"]+)"?/i.exec(cd);
|
||||
const filename = match ? match[1] : `amnezia-migrate-${SERVER_ID}.zip`;
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
showToast(_('migrate_export_done'), 'success');
|
||||
} catch (err) {
|
||||
showToast(`${_('error')}: ${err.message}`, 'error');
|
||||
} finally {
|
||||
if (btn) btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function importServerMigrate(e) {
|
||||
const file = e.target.files && e.target.files[0];
|
||||
e.target.value = '';
|
||||
if (!file) return;
|
||||
if (!confirm(_('migrate_import_confirm'))) return;
|
||||
const btn = document.getElementById('migrateImportBtn');
|
||||
if (btn) btn.disabled = true;
|
||||
showToast(_('migrate_importing'), 'info');
|
||||
try {
|
||||
const form = new FormData();
|
||||
form.append('file', file);
|
||||
const res = await fetch(`/api/servers/${SERVER_ID}/migrate/import?restore_protocols=true`, {
|
||||
method: 'POST',
|
||||
body: form,
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (!res.ok || data.status !== 'success') {
|
||||
throw new Error(data.error || `HTTP ${res.status}`);
|
||||
}
|
||||
const added = (data.connections && data.connections.added) || 0;
|
||||
const protos = (data.protocols_restored || []).join(', ') || '—';
|
||||
showToast(
|
||||
`${_('migrate_import_done')} (+${added} ${_('connections') || 'conn'}; ${protos})`,
|
||||
'success'
|
||||
);
|
||||
if (data.connect_domain) {
|
||||
showToast(`${_('migrate_dns_hint')}: ${data.connect_domain}`, 'info');
|
||||
}
|
||||
setTimeout(() => checkServer(), 1500);
|
||||
} catch (err) {
|
||||
showToast(`${_('error')}: ${err.message}`, 'error');
|
||||
} finally {
|
||||
if (btn) btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function openManagementModal() {
|
||||
renderManagementModal();
|
||||
openModal('managementModal');
|
||||
|
||||
Reference in New Issue
Block a user