fix: generate Caddyfile in setup and on identity changes

`make reinstall` wipes config/ then `make setup` creates an empty
Caddyfile (ensure_file just touches it). Add write_caddyfile() to
ip_utils.py that generates the full reverse-proxy config from ip_range,
cell_name, and domain. Call it from setup_cell.py so fresh installs
always get a valid Caddyfile. Also regenerate it in app.py whenever
ip_range, domain, or cell_name changes so Caddy stays in sync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 15:18:37 -04:00
parent c9ed28f258
commit e74d5e0504
3 changed files with 105 additions and 1 deletions
+78
View File
@@ -129,6 +129,84 @@ def get_virtual_ips(ip_range: str) -> Dict[str, str]:
}
def write_caddyfile(ip_range: str, cell_name: str, domain: str, path: str) -> bool:
"""
Generate the Caddy reverse-proxy config from the current ip_range, cell_name, and domain.
Must be called after any ip_range or domain change so Caddy routes correctly.
Container-internal ports are fixed by docker-compose and never change.
Returns True on success.
"""
try:
ips = get_service_ips(ip_range)
caddy_ip = ips['caddy']
vip_calendar = ips['vip_calendar']
vip_files = ips['vip_files']
vip_mail = ips['vip_mail']
vip_webdav = ips['vip_webdav']
content = f"""\
{{
auto_https off
}}
# Main cell domain — no service-IP restriction needed
http://{cell_name}.{domain}, http://{caddy_ip}:80 {{
handle /api/* {{
reverse_proxy cell-api:3000
}}
handle /calendar* {{
reverse_proxy cell-radicale:5232
}}
handle /files* {{
reverse_proxy cell-filegator:8080
}}
handle /webmail* {{
reverse_proxy cell-rainloop:8888
}}
handle {{
reverse_proxy cell-webui:80
}}
}}
# Per-service virtual IPs — each gets its own IP so iptables can target them
http://calendar.{domain}, http://{vip_calendar}:80 {{
reverse_proxy cell-radicale:5232
}}
http://files.{domain}, http://{vip_files}:80 {{
reverse_proxy cell-filegator:8080
}}
http://mail.{domain}, http://webmail.{domain}, http://{vip_mail}:80 {{
reverse_proxy cell-rainloop:8888
}}
http://webdav.{domain}, http://{vip_webdav}:80 {{
reverse_proxy cell-webdav:80
}}
http://api.{domain} {{
reverse_proxy cell-api:3000
}}
# Catch-all for direct IP / localhost
:80 {{
handle /api/* {{
reverse_proxy cell-api:3000
}}
handle {{
reverse_proxy cell-webui:80
}}
}}
"""
os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True)
with open(path, 'w') as f:
f.write(content)
return True
except Exception:
return False
def write_env_file(ip_range: str, path: str, ports: Optional[Dict[str, int]] = None) -> bool:
"""
Write (or overwrite) the docker-compose .env file with IPs and ports.