feat: Settings changes now apply to real service config files and restart containers

Each service manager now has apply_config() that writes to the actual config:
- network: dhcp_range → dnsmasq.conf (reload cell-dhcp), ntp_servers → chrony.conf
  (restart cell-ntp), domain → dnsmasq.conf domain= line
- email: domain → mailserver.env OVERRIDE_HOSTNAME + POSTMASTER_ADDRESS,
  restart cell-mail
- wireguard: port/address/private_key → wg0.conf ListenPort/Address/PrivateKey,
  restart cell-wireguard
- calendar: port → radicale config hosts=, restart cell-radicale

PUT /api/config now calls apply_config() after persisting JSON, and returns
{restarted: [...], warnings: [...]} so Settings UI can show which containers
were restarted. _restart_container() helper added to BaseServiceManager.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-21 04:27:22 -04:00
parent ae73246878
commit 87ff50c378
7 changed files with 237 additions and 12 deletions
+23
View File
@@ -477,6 +477,29 @@ class CalendarManager(BaseServiceManager):
with open(config_file, 'w') as f:
f.write(config_content)
def apply_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
"""Update radicale config port and restart cell-radicale."""
restarted = []
warnings = []
if 'port' not in config:
return {'restarted': restarted, 'warnings': warnings}
try:
radicale_conf = os.path.join(self.radicale_dir, 'config')
if os.path.exists(radicale_conf):
with open(radicale_conf) as f:
lines = f.readlines()
lines = [
f"hosts = 0.0.0.0:{config['port']}\n" if l.strip().startswith('hosts =') else l
for l in lines
]
with open(radicale_conf, 'w') as f:
f.writelines(lines)
self._restart_container('cell-radicale')
restarted.append('cell-radicale')
except Exception as e:
warnings.append(f"radicale config update failed: {e}")
return {'restarted': restarted, 'warnings': warnings}
def remove_calendar(self, username: str, calendar_name: str) -> bool:
"""Remove a calendar."""
try: