Add i18n, branding, user management, health checks, and cleanup for deployment

- Multi-language support (EN/DE) with i18n engine and language files
- Configurable branding (name, subtitle, logo) in Settings
- Global default language and per-user language preference
- User management router with CRUD endpoints
- Customer status sync on start/stop/restart
- Health check fixes: derive status from container state, remove broken wget healthcheck
- Caddy reverse proxy and dashboard env templates for customer stacks
- Updated README with real hardware specs, prerequisites, and new features
- Removed .claude settings (JWT tokens) and build artifacts from tracking
- Updated .gitignore for .claude/ and Windows artifacts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 17:24:05 +01:00
parent c4d68db2f4
commit 41ba835a99
28 changed files with 2550 additions and 661 deletions

View File

@@ -26,12 +26,20 @@ def _get_client() -> docker.DockerClient:
return docker.from_env()
def compose_up(instance_dir: str, project_name: str) -> bool:
def compose_up(
instance_dir: str,
project_name: str,
services: Optional[list[str]] = None,
timeout: int = 300,
) -> bool:
"""Run ``docker compose up -d`` for a customer instance.
Args:
instance_dir: Absolute path to the customer's instance directory.
project_name: Docker Compose project name (e.g. ``netbird-kunde5``).
services: Optional list of service names to start.
If None, all services are started.
timeout: Subprocess timeout in seconds (default 300).
Returns:
True on success.
@@ -47,16 +55,22 @@ def compose_up(instance_dir: str, project_name: str) -> bool:
"docker", "compose",
"-f", compose_file,
"-p", project_name,
"up", "-d", "--remove-orphans",
"up", "-d",
]
if not services:
cmd.append("--remove-orphans")
if services:
cmd.extend(services)
logger.info("Running: %s", " ".join(cmd))
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
if result.returncode != 0:
logger.error("docker compose up failed: %s", result.stderr)
raise RuntimeError(f"docker compose up failed: {result.stderr}")
logger.info("docker compose up succeeded for %s", project_name)
svc_info = f" (services: {', '.join(services)})" if services else ""
logger.info("docker compose up succeeded for %s%s", project_name, svc_info)
return True
@@ -169,9 +183,13 @@ def get_container_status(container_prefix: str) -> list[dict[str, Any]]:
try:
containers = client.containers.list(all=True, filters={"name": container_prefix})
for c in containers:
health = "N/A"
if c.attrs.get("State", {}).get("Health"):
health = c.attrs["State"]["Health"].get("Status", "N/A")
# Derive health from container status.
# Docker HEALTHCHECK is unreliable (e.g. netbirdio/management
# defines a wget-based check but wget is not installed).
if c.status == "running":
health = "healthy"
else:
health = "unhealthy"
results.append({
"name": c.name,
"status": c.status,