feat(deploy): redeploy dialog with keep-data or fresh-deploy option

Add a confirmation modal when clicking Redeploy that lets the user choose:
- Keep Data: containers are recreated without wiping the instance directory.
  NetBird database, peer configs, and encryption keys are preserved.
- Fresh Deploy: full undeploy (removes all data) then redeploy from scratch.

Backend changes:
- POST /customers/{id}/deploy accepts keep_data query param (default false)
- When keep_data=true, undeploy_customer is skipped entirely
- deploy_customer now reuses existing npm_proxy_id/stream_id when the
  deployment record is still present (avoids duplicate NPM proxy entries)
- DNS record creation is skipped on keep_data redeploy (already exists)

Frontend changes:
- customerAction('deploy') opens the redeploy modal instead of calling API
- showRedeployModal(id) shows the two-option confirmation card dialog
- confirmRedeploy(keepData) calls the API with the correct parameter
- i18n keys added in en.json and de.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 21:34:12 +01:00
parent dee07d7b8e
commit d1bb6a633e
6 changed files with 117 additions and 12 deletions

View File

@@ -667,9 +667,13 @@ async function confirmDeleteCustomer() {
}
// ---------------------------------------------------------------------------
// Customer Actions (start/stop/restart)
// Customer Actions (start/stop/restart/deploy)
// ---------------------------------------------------------------------------
async function customerAction(id, action) {
if (action === 'deploy') {
showRedeployModal(id);
return;
}
try {
await api('POST', `/customers/${id}/${action}`);
if (currentPage === 'dashboard') loadCustomers();
@@ -679,6 +683,26 @@ async function customerAction(id, action) {
}
}
function showRedeployModal(id) {
const row = document.querySelector(`tr[data-customer-id="${id}"]`);
const name = row ? row.querySelector('td')?.textContent?.trim() : `#${id}`;
document.getElementById('redeploy-customer-id').value = id;
document.getElementById('redeploy-customer-name').textContent = name;
new bootstrap.Modal(document.getElementById('redeploy-modal')).show();
}
async function confirmRedeploy(keepData) {
const id = document.getElementById('redeploy-customer-id').value;
bootstrap.Modal.getInstance(document.getElementById('redeploy-modal'))?.hide();
try {
await api('POST', `/customers/${id}/deploy?keep_data=${keepData}`);
if (currentPage === 'dashboard') loadCustomers();
if (currentCustomerId == id) viewCustomer(id);
} catch (err) {
alert(t('errors.actionFailed', { action: 'deploy', error: err.message }));
}
}
// ---------------------------------------------------------------------------
// Customer Detail
// ---------------------------------------------------------------------------