Compare commits

...

8 Commits

Author SHA1 Message Date
7d694c62bd feat(deploy): redeploy dialog with keep-data or fresh-deploy option
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 21:34:26 +01:00
d1bb6a633e 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>
2026-03-10 21:34:12 +01:00
b39a502257 fix(images): use Docker Registry v2 API for correct digest comparison
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 15:15:22 +01:00
dee07d7b8e fix(images): use Docker Registry v2 API for correct digest comparison
The Docker Hub REST API returns per-platform manifest digests, while
docker image inspect RepoDigests stores the manifest list digest.
These two values never match, causing update_available to always be
True even after a fresh pull.

Fix: use registry-1.docker.io/v2/{name}/manifests/{tag} with anonymous
auth and read the Docker-Content-Digest response header, which is the
exact same digest that docker pull stores in RepoDigests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 15:15:05 +01:00
351caec893 docs: update README with all current features and correct settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 08:34:29 +01:00
dd04408dc2 docs: update README with all current features and correct settings
- Add NetBird Container Updates section (digest check, pull, bulk update)
- Add update indicators, dark mode, LDAP/AD, Windows DNS to features
- Correct Settings table: add all tabs, remove incorrect Monitoring entry
- Split Docker Images tab out of System tab
- Add sudo install note for fresh Debian minimal
- Rewrite Updating NetBird Images section with new UI-based workflow
- Add new monitoring/image API endpoints to API docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 08:33:20 +01:00
6373722c2b chore(release): merge unstable → main for beta-1.0
Promotes alpha-1.25 to beta-1.0 (stable branch).

Highlights:
- NetBird container update management (check / pull / update per customer + bulk)
- Visual update badges on dashboard and customer detail
- Dark mode toggle with localStorage persistence
- User role management for Azure AD / LDAP users
- Branding logo persistence across updates (Docker volume)
- Favicon, NPM stream removal, MFA (TOTP)
- LDAP / Active Directory and Azure AD SSO
- Windows DNS integration
- Settings restructure and Git branch dropdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 21:58:24 +01:00
2713e67259 Deutsch korrektur 2026-02-09 15:55:01 +01:00
8 changed files with 317 additions and 75 deletions

207
README.md
View File

@@ -38,11 +38,20 @@ A management solution for running isolated NetBird instances for your MSP busine
- **Docker-Based** — Everything runs in containers for easy deployment
### Dashboard
- **Modern Web UI** — Responsive Bootstrap 5 interface
- **Modern Web UI** — Responsive Bootstrap 5 interface with dark/light mode toggle
- **Real-Time Monitoring** — Container status, health checks, resource usage
- **Container Logs** — View logs per container directly in the browser
- **Start / Stop / Restart** — Control customer instances from the dashboard
- **Customer Status Tracking** — Automatic status sync (active / inactive / error)
- **Update Indicators** — Per-customer badges when container images are outdated
### NetBird Container Updates
- **Docker Hub Digest Check** — Compare locally pulled image digests against Docker Hub without pulling
- **One-Click Pull** — Pull all NetBird images from Docker Hub via Settings
- **Bulk Update** — Update all outdated customer containers at once from the Monitoring page
- **Per-Customer Update** — Update a single customer's containers from the customer detail view
- **Zero Data Loss** — Container recreation preserves all bind-mounted volumes
- **Sequential Updates** — Customers are updated one at a time to minimize risk
### Multi-Language (i18n)
- **English and German** — Full UI translation
@@ -55,13 +64,18 @@ A management solution for running isolated NetBird instances for your MSP busine
- **Login Page** — Branding is applied to the login page automatically
- **Configurable Docker Images** — Use custom or specific NetBird image versions
### Security
### Authentication & User Management
- **JWT Authentication** — Token-based API authentication
- **Multi-Factor Authentication (MFA)** — Optional TOTP-based MFA for all local users, activatable in Security settings
- **Azure AD / OIDC** — Optional single sign-on via Microsoft Entra ID (exempt from MFA)
- **Encrypted Credentials** — NPM passwords, relay secrets, and TOTP secrets are Fernet-encrypted at rest
- **LDAP / Active Directory** — Allow AD users to authenticate; local admin accounts always work as fallback
- **Encrypted Credentials** — NPM passwords, relay secrets, TOTP secrets, and LDAP bind passwords are Fernet-encrypted at rest
- **User Management** — Create, edit, delete admin users, reset passwords and MFA
### Integrations
- **Windows DNS** — Automatically create and delete DNS A-records when deploying or removing customers
- **MSP Updates** — In-UI appliance update check with configurable release branch
---
## Architecture
@@ -178,6 +192,18 @@ The following tools and services must be available **before** running the instal
### Install Prerequisites (Ubuntu/Debian)
> **Note:** On a fresh Debian minimal install, `sudo` is not pre-installed. Install it as root first:
```bash
# As root — only needed on fresh Debian minimal (sudo not pre-installed):
apt update && apt install -y sudo
# Install remaining prerequisites:
sudo apt install -y curl git openssl
```
If `sudo` is already available (Ubuntu, most standard installs):
```bash
sudo apt update
sudo apt install -y curl git openssl
@@ -266,17 +292,32 @@ HOST_IP=<your-server-ip>
### Web UI Settings
Available under **Settings** in the web interface:
Available under **Settings** in the web interface, organized into tabs:
#### User Management
| Tab | Settings |
|-----|----------|
| **System** | Base domain, admin email, Docker images, port ranges, data directory |
| **NPM Integration** | NPM API URL, login credentials, SSL certificate mode (Let's Encrypt / Wildcard), wildcard certificate selection |
| **Branding** | Platform name, subtitle, logo upload, default language |
| **Azure AD** | Azure AD / Entra ID SSO configuration (tenant ID, client ID/secret, optional group restriction) |
| **Users** | Create/edit/delete admin users, per-user language preference, MFA reset |
| **Azure AD** | Azure AD / Entra ID SSO configuration |
| **LDAP / AD** | LDAP/Active Directory authentication (server, base DN, bind credentials, group restriction), enable/disable |
| **Security** | Change admin password, enable/disable MFA globally, manage own TOTP |
| **Monitoring** | System resources, Docker stats |
#### System
| Tab | Settings |
|-----|----------|
| **Branding** | Platform name, subtitle, logo upload, default language |
| **NetBird Docker Images** | Configured NetBird image tags (management, signal, relay, dashboard), pull images from Docker Hub |
| **NetBird MSP System** | Base domain, admin email, port ranges, data directory |
| **NetBird MSP Updates** | Appliance version info, check for updates, switch release branch |
#### External Systems
| Tab | Settings |
|-----|----------|
| **NPM Proxy** | NPM API URL, login credentials, SSL certificate mode (Let's Encrypt / Wildcard), wildcard certificate selection |
| **Windows DNS** | Windows DNS server integration for automatic DNS A-record creation/deletion on customer deploy/delete |
Changes are applied immediately without restart.
@@ -308,11 +349,42 @@ Changes are applied immediately without restart.
### Monitoring
The dashboard shows:
The **Monitoring** page shows:
- **System Overview** — Total customers, active/inactive, errors
- **Resource Usage** — RAM, CPU per container
- **Container Health** — Running/stopped per container with color-coded status
- **Deployment Logs** — Action history per customer
- **Host Resources** — CPU, RAM, disk usage of the host machine
- **Customer Status** — Container health per customer (running/stopped)
- **NetBird Container Updates** — Compare local image digests against Docker Hub, pull new images, and update all outdated customer containers
### NetBird Container Updates
#### Workflow
1. **Check for updates** — Go to **Monitoring > NetBird Container Updates**, click **"Check Updates"**
- Compares local image digests against Docker Hub
- Shows which images have a new version available
- Shows which customer containers are running outdated images
- An orange badge appears next to customers in the dashboard list that need updating
2. **Pull new images** — Go to **Settings > NetBird Docker Images**, click **"Pull from Docker Hub"**
- Pulls all 4 NetBird images (`management`, `signal`, `relay`, `dashboard`) in the background
- Wait for the pull to complete before updating customers
3. **Update customers** — Return to **Monitoring > NetBird Container Updates**, click **"Update All Customers"**
- Recreates containers for all customers whose running image is outdated
- Customers are updated **sequentially** — one at a time
- All bind-mounted volumes (database, keys, config) are preserved — **no data loss**
- A per-customer results table is shown after completion
#### Per-Customer Update
To update a single customer:
1. Open the customer detail view
2. Go to the **Deployment** tab
3. Click **"Update Images"**
#### Update Badges
The dashboard customer list shows an orange **"Update"** badge next to any customer whose running containers are using an outdated local image. This check is fast (local-only, no network call) and runs automatically when the dashboard loads.
### Language Settings
@@ -320,9 +392,13 @@ The dashboard shows:
- **Per-user default** — Set in Settings > Users during user creation
- **System default** — Set in Settings > Branding
### Dark Mode
Toggle dark/light mode using the moon/sun icon in the top navigation bar. The preference is saved in the browser.
### Multi-Factor Authentication (MFA)
TOTP-based MFA can be enabled globally for all local users. Azure AD users are not affected (they use their own MFA).
TOTP-based MFA can be enabled globally for all local users. Azure AD and LDAP users are not affected (they use their own authentication systems).
#### Enable MFA
1. Go to **Settings > Security**
@@ -344,9 +420,30 @@ When MFA is enabled and a user logs in for the first time:
- **Disable own TOTP** — In Settings > Security, click "Disable my TOTP" to remove your own MFA setup
- **Disable MFA globally** — Uncheck the toggle in Settings > Security to allow login without MFA
### LDAP / Active Directory Authentication
Active Directory users can log in to the appliance using their AD credentials. Local admin accounts always work as a fallback regardless of LDAP status.
#### Setup
1. Go to **Settings > LDAP / AD**
2. Enable **"LDAP / AD Authentication"**
3. Enter LDAP server, port, bind DN (service account), bind password, and base DN
4. Optionally restrict access to members of a specific AD group
5. Click **Save LDAP Settings**
### Windows DNS Integration
Automatically create and delete DNS A-records in a Windows DNS server when customers are deployed or deleted.
#### Setup
1. Go to **Settings > Windows DNS**
2. Enable **"Windows DNS Integration"**
3. Enter the DNS server details
4. Click **Save DNS Settings**
### SSL Certificate Mode
The appliance supports two SSL certificate modes for customer proxy hosts, configurable under **Settings > NPM Integration**:
The appliance supports two SSL certificate modes for customer proxy hosts, configurable under **Settings > NPM Proxy**:
#### Let's Encrypt (default)
Each customer gets an individual Let's Encrypt certificate via HTTP-01 validation. This is the default behavior and requires no additional setup beyond a valid admin email.
@@ -356,7 +453,7 @@ Use a pre-existing wildcard certificate (e.g. `*.yourdomain.com`) already upload
**Setup:**
1. Upload a wildcard certificate in Nginx Proxy Manager (e.g. via DNS challenge)
2. Go to **Settings > NPM Integration**
2. Go to **Settings > NPM Proxy**
3. Set **SSL Mode** to "Wildcard Certificate"
4. Click the refresh button to load certificates from NPM
5. Select your wildcard certificate from the dropdown
@@ -385,30 +482,37 @@ http://your-server:8000/api/docs
**Common Endpoints:**
```
POST /api/customers # Create customer + deploy
GET /api/customers # List all customers
GET /api/customers/{id} # Get customer details
PUT /api/customers/{id} # Update customer
DELETE /api/customers/{id} # Delete customer
POST /api/customers # Create customer + deploy
GET /api/customers # List all customers
GET /api/customers/{id} # Get customer details
PUT /api/customers/{id} # Update customer
DELETE /api/customers/{id} # Delete customer
POST /api/customers/{id}/start # Start containers
POST /api/customers/{id}/stop # Stop containers
POST /api/customers/{id}/restart # Restart containers
GET /api/customers/{id}/logs # Get container logs
GET /api/customers/{id}/health # Health check
POST /api/customers/{id}/start # Start containers
POST /api/customers/{id}/stop # Stop containers
POST /api/customers/{id}/restart # Restart containers
GET /api/customers/{id}/logs # Get container logs
GET /api/customers/{id}/health # Health check
POST /api/customers/{id}/update-images # Recreate containers with new images
GET /api/settings/branding # Get branding (public, no auth)
GET /api/settings/npm-certificates # List NPM SSL certificates
PUT /api/settings # Update system settings
GET /api/users # List users
POST /api/users # Create user
POST /api/users/{id}/reset-mfa # Reset user's MFA
GET /api/settings/branding # Get branding (public, no auth)
GET /api/settings/npm-certificates # List NPM SSL certificates
PUT /api/settings # Update system settings
POST /api/auth/mfa/setup # Generate TOTP secret + QR code
POST /api/auth/mfa/setup/complete # Verify first TOTP code
POST /api/auth/mfa/verify # Verify TOTP code on login
GET /api/auth/mfa/status # Get MFA status
POST /api/auth/mfa/disable # Disable own TOTP
GET /api/users # List users
POST /api/users # Create user
POST /api/users/{id}/reset-mfa # Reset user's MFA
POST /api/auth/mfa/setup # Generate TOTP secret + QR code
POST /api/auth/mfa/setup/complete # Verify first TOTP code
POST /api/auth/mfa/verify # Verify TOTP code on login
GET /api/auth/mfa/status # Get MFA status
POST /api/auth/mfa/disable # Disable own TOTP
GET /api/monitoring/images/check # Check Hub vs local digests for all images
POST /api/monitoring/images/pull # Pull all NetBird images from Docker Hub (background)
GET /api/monitoring/customers/local-update-status # Fast local-only update check (no network)
POST /api/monitoring/customers/update-all # Recreate outdated containers for all customers
```
### Example: Create Customer via API
@@ -488,11 +592,28 @@ The database migrations run automatically on startup.
### Updating NetBird Images
Via the Web UI:
1. Settings > System Configuration
2. Change image tags (e.g., `netbirdio/management:0.35.0`)
3. Click "Save"
4. Re-deploy individual customers to apply the new images
NetBird image updates are managed entirely through the Web UI — no manual config changes required.
#### Step 1 — Pull new images
1. Go to **Settings > NetBird Docker Images**
2. Click **"Pull from Docker Hub"**
3. Wait for the pull to complete (progress shown inline)
#### Step 2 — Check which customers need updating
1. Go to **Monitoring > NetBird Container Updates**
2. Click **"Check Updates"**
3. The table shows per-image Hub vs. local digest comparison and which customers are running outdated containers
#### Step 3 — Update customer containers
- **All customers**: Click **"Update All Customers"** in the Monitoring page
- Customers are updated sequentially, one at a time
- A results table is shown after completion
- **Single customer**: Open the customer detail view > **Deployment** tab > **"Update Images"**
> All bind-mounted volumes (database, keys, config files) are preserved. Container recreation does not cause data loss.
---
@@ -546,7 +667,7 @@ MIT License — see [LICENSE](LICENSE) file for details.
## Built With AI
This software was developed with [Claude Code](https://claude.ai/claude-code) (Anthropic Claude Opus 4.6) — from architecture and backend logic to frontend UI and deployment scripts.
This software was developed with [Claude Code](https://claude.ai/claude-code) (Anthropic Claude Sonnet 4.6) — from architecture and backend logic to frontend UI and deployment scripts.
## Acknowledgments

View File

@@ -2,7 +2,7 @@
import logging
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query, status
from sqlalchemy.orm import Session
from app.database import SessionLocal, get_db
@@ -19,6 +19,14 @@ router = APIRouter()
async def manual_deploy(
customer_id: int,
background_tasks: BackgroundTasks,
keep_data: bool = Query(
False,
description=(
"If True, preserve existing NetBird data (database, keys, peers). "
"Containers are recreated without wiping the instance directory. "
"If False (default), the instance is fully removed and redeployed from scratch."
),
),
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
@@ -29,6 +37,7 @@ async def manual_deploy(
Args:
customer_id: Customer ID.
keep_data: Whether to preserve existing NetBird data.
Returns:
Acknowledgement dict.
@@ -40,12 +49,12 @@ async def manual_deploy(
customer.status = "deploying"
db.commit()
async def _deploy_bg(cid: int) -> None:
async def _deploy_bg(cid: int, keep: bool) -> None:
bg_db = SessionLocal()
try:
# Remove existing deployment if present
existing = bg_db.query(Deployment).filter(Deployment.customer_id == cid).first()
if existing:
if existing and not keep:
# Full redeploy: remove everything first
await netbird_service.undeploy_customer(bg_db, cid)
await netbird_service.deploy_customer(bg_db, cid)
except Exception:
@@ -53,7 +62,7 @@ async def manual_deploy(
finally:
bg_db.close()
background_tasks.add_task(_deploy_bg, customer_id)
background_tasks.add_task(_deploy_bg, customer_id, keep_data)
return {"message": "Deployment started in background.", "status": "deploying"}

View File

@@ -38,33 +38,49 @@ def _parse_image_name(image: str) -> tuple[str, str]:
async def get_hub_digest(image: str) -> str | None:
"""Fetch the current digest from Docker Hub for an image:tag.
"""Fetch the manifest-list digest from the Docker Registry v2 API.
Uses the Docker Hub REST API — does NOT pull the image.
Returns the digest string (sha256:...) or None on failure.
Uses anonymous auth against registry-1.docker.io — does NOT pull the image.
Returns the Docker-Content-Digest header value (sha256:...) which is identical
to the digest stored in local RepoDigests after a pull, enabling correct comparison.
"""
name, tag = _parse_image_name(image)
url = f"https://hub.docker.com/v2/repositories/{name}/tags/{tag}/"
try:
async with httpx.AsyncClient(timeout=15) as client:
resp = await client.get(url)
if resp.status_code != 200:
logger.warning("Docker Hub API returned %d for %s", resp.status_code, image)
# Step 1: obtain anonymous pull token
token_resp = await client.get(
"https://auth.docker.io/token",
params={"service": "registry.docker.io", "scope": f"repository:{name}:pull"},
)
if token_resp.status_code != 200:
logger.warning("Failed to get registry token for %s", image)
return None
token = token_resp.json().get("token")
# Step 2: fetch manifest — prefer manifest list (multi-arch) so the digest
# matches what `docker pull` stores in RepoDigests.
manifest_resp = await client.get(
f"https://registry-1.docker.io/v2/{name}/manifests/{tag}",
headers={
"Authorization": f"Bearer {token}",
"Accept": (
"application/vnd.docker.distribution.manifest.list.v2+json, "
"application/vnd.oci.image.index.v1+json, "
"application/vnd.docker.distribution.manifest.v2+json"
),
},
)
if manifest_resp.status_code != 200:
logger.warning("Registry API returned %d for %s", manifest_resp.status_code, image)
return None
# The Docker-Content-Digest header is the canonical digest
digest = manifest_resp.headers.get("docker-content-digest")
if digest:
return digest
return None
data = resp.json()
images = data.get("images", [])
# Prefer linux/amd64 digest
for img in images:
if img.get("os") == "linux" and img.get("architecture") in ("amd64", ""):
d = img.get("digest")
if d:
return d
# Fallback: first available digest
if images:
return images[0].get("digest")
return None
except Exception as exc:
logger.warning("Failed to fetch Docker Hub digest for %s: %s", image, exc)
logger.warning("Failed to fetch registry digest for %s: %s", image, exc)
return None

View File

@@ -264,10 +264,12 @@ async def deploy_customer(db: Session, customer_id: int) -> dict[str, Any]:
_log_action(db, customer_id, "deploy", "info",
"Auto-setup failed — admin must complete setup manually.")
# Step 9: Create NPM proxy host (production only)
npm_proxy_id = None
npm_stream_id = None
if not local_mode:
# Step 9: Create NPM proxy host (production only).
# If an existing deployment already has an NPM proxy, reuse it — this happens
# when keep_data=True was passed and undeploy_customer was NOT called beforehand.
npm_proxy_id = existing_deployment.npm_proxy_id if existing_deployment else None
npm_stream_id = existing_deployment.npm_stream_id if existing_deployment else None
if not local_mode and not npm_proxy_id:
forward_host = npm_service._get_forward_host()
npm_result = await npm_service.create_proxy_host(
api_url=config.npm_api_url,
@@ -304,9 +306,14 @@ async def deploy_customer(db: Session, customer_id: int) -> dict[str, Any]:
"SSL certificate not created automatically. "
"Please create it manually in NPM or ensure DNS resolves and port 80 is reachable, then re-deploy.",
)
elif npm_proxy_id and not local_mode:
_log_action(db, customer_id, "deploy", "info",
f"Reusing existing NPM proxy (ID {npm_proxy_id}) — data preserved.")
# Step 10: Create Windows DNS A-record (non-fatal — failure does not abort deployment)
if config.dns_enabled and config.dns_server and config.dns_zone and config.dns_record_ip:
# Step 10: Create Windows DNS A-record (non-fatal — failure does not abort deployment).
# Skip if an existing deployment is being kept (DNS record already exists).
if config.dns_enabled and config.dns_server and config.dns_zone and config.dns_record_ip \
and not existing_deployment:
try:
dns_result = await dns_service.create_dns_record(customer.subdomain, config)
if dns_result["ok"]:

View File

@@ -1267,6 +1267,49 @@
</div>
</div>
<!-- Modal: Redeploy Confirmation -->
<div class="modal fade" id="redeploy-modal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title" data-i18n="redeployModal.title">Redeploy Customer</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p><span data-i18n="redeployModal.intro">How should</span> <strong id="redeploy-customer-name"></strong> <span data-i18n="redeployModal.intro2">be redeployed?</span></p>
<input type="hidden" id="redeploy-customer-id">
<div class="row g-3 mt-1">
<div class="col-md-6">
<div class="card h-100 border-success" style="cursor:pointer" onclick="confirmRedeploy(true)" id="redeploy-card-keep">
<div class="card-body">
<h6 class="card-title text-success"><i class="bi bi-shield-check me-2"></i><span data-i18n="redeployModal.keepTitle">Keep Data</span></h6>
<p class="card-text small" data-i18n="redeployModal.keepDesc">Containers are stopped and restarted. The NetBird database, peer configurations, and encryption keys are preserved. Use this after a config change or image update.</p>
</div>
<div class="card-footer bg-success bg-opacity-10 text-success small" data-i18n="redeployModal.keepNote">
Peers stay connected after restart.
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100 border-danger" style="cursor:pointer" onclick="confirmRedeploy(false)" id="redeploy-card-fresh">
<div class="card-body">
<h6 class="card-title text-danger"><i class="bi bi-trash me-2"></i><span data-i18n="redeployModal.freshTitle">Fresh Deploy</span></h6>
<p class="card-text small" data-i18n="redeployModal.freshDesc">All existing data is deleted — containers, volumes, config files, and the NetBird database. A completely new instance is created. All peers must re-enroll.</p>
</div>
<div class="card-footer bg-danger bg-opacity-10 text-danger small" data-i18n="redeployModal.freshNote">
All peer data is lost. Cannot be undone.
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" data-i18n="common.cancel">Cancel</button>
</div>
</div>
</div>
</div>
<!-- Modal: Delete Confirmation -->
<div class="modal fade" id="delete-modal" tabindex="-1">
<div class="modal-dialog">

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
// ---------------------------------------------------------------------------

View File

@@ -354,6 +354,17 @@
"saveAndDeploy": "Speichern & Bereitstellen",
"saveChanges": "Änderungen speichern"
},
"redeployModal": {
"title": "Kunde neu bereitstellen",
"intro": "Wie soll",
"intro2": "neu bereitgestellt werden?",
"keepTitle": "Daten behalten",
"keepDesc": "Container werden gestoppt und neu gestartet. Die NetBird-Datenbank, Peer-Konfigurationen und Verschlüsselungsschlüssel bleiben erhalten. Verwenden Sie dies nach einer Konfigurationsänderung oder einem Image-Update.",
"keepNote": "Peers bleiben nach dem Neustart verbunden.",
"freshTitle": "Neu aufsetzen",
"freshDesc": "Alle bestehenden Daten werden gelöscht — Container, Volumes, Konfigurationsdateien und die NetBird-Datenbank. Eine komplett neue Instanz wird erstellt. Alle Peers müssen sich neu registrieren.",
"freshNote": "Alle Peer-Daten gehen verloren. Kann nicht rückgängig gemacht werden."
},
"deleteModal": {
"title": "Löschen bestätigen",
"confirmText": "Möchten Sie den Kunden wirklich löschen:",

View File

@@ -107,6 +107,17 @@
"saveAndDeploy": "Save & Deploy",
"saveChanges": "Save Changes"
},
"redeployModal": {
"title": "Redeploy Customer",
"intro": "How should",
"intro2": "be redeployed?",
"keepTitle": "Keep Data",
"keepDesc": "Containers are stopped and restarted. The NetBird database, peer configurations, and encryption keys are preserved. Use this after a config change or image update.",
"keepNote": "Peers stay connected after restart.",
"freshTitle": "Fresh Deploy",
"freshDesc": "All existing data is deleted — containers, volumes, config files, and the NetBird database. A completely new instance is created. All peers must re-enroll.",
"freshNote": "All peer data is lost. Cannot be undone."
},
"deleteModal": {
"title": "Confirm Deletion",
"confirmText": "Are you sure you want to delete customer",