fix: propagate Settings config changes to service managers and live pages

- PUT /api/config now calls service_manager.update_config() for each service
  so changes write to the service's own config file, not just cell_config.json
- email_manager.get_status() now reads smtp_port/imap_port/domain from its
  config file (defaults: 587/993/cell.local) and includes them in the response
- calendar_manager.get_status() includes configured port (default 5232)
- file_manager.get_status() uses configured port from service config
- Email.jsx reads imap_port/smtp_port from API status instead of hardcoding
- Settings service sections show "port changes require container restart" note

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-21 03:46:31 -04:00
parent c778ee8eb8
commit ae73246878
6 changed files with 61 additions and 22 deletions
+21 -7
View File
@@ -403,15 +403,29 @@ def update_config():
config_manager.configs['_identity'] = stored
config_manager._save_all_configs()
# Update service configurations
# Map service names to their manager instances
_svc_managers = {
'network': network_manager,
'wireguard': wireguard_manager,
'email': email_manager,
'calendar': calendar_manager,
'files': file_manager,
'routing': routing_manager,
'vault': app.vault_manager,
}
# Update service configurations in both config_manager and service managers
for service, config in data.items():
if service in config_manager.service_schemas:
success = config_manager.update_service_config(service, config)
if success:
service_bus.publish_event(EventType.CONFIG_CHANGED, service, {
'service': service,
'config': config
})
config_manager.update_service_config(service, config)
# Propagate to the service manager's own config file
mgr = _svc_managers.get(service)
if mgr:
mgr.update_config(config)
service_bus.publish_event(EventType.CONFIG_CHANGED, service, {
'service': service,
'config': config
})
logger.info(f"Updated config: {data}")
return jsonify({"message": "Configuration updated successfully"})
+12 -3
View File
@@ -29,19 +29,27 @@ class CalendarManager(BaseServiceManager):
self.safe_makedirs(self.calendar_data_dir)
self.safe_makedirs(self.radicale_dir)
def _get_configured_port(self) -> int:
cfg = self.get_config()
if isinstance(cfg, dict) and 'error' not in cfg:
return cfg.get('port', 5232)
return 5232
def get_status(self) -> Dict[str, Any]:
"""Get calendar service status"""
try:
port = self._get_configured_port()
# Check if we're running in Docker environment
import os
is_docker = os.path.exists('/.dockerenv') or os.environ.get('DOCKER_CONTAINER') == 'true'
if is_docker:
# Check if calendar container is actually running
container_running = self._check_calendar_container_status()
status = {
'running': container_running,
'status': 'online' if container_running else 'offline',
'port': port,
'users_count': 0,
'calendars_count': 0,
'events_count': 0,
@@ -53,16 +61,17 @@ class CalendarManager(BaseServiceManager):
users = self._load_users()
calendars = self._load_calendars()
events = self._load_events()
status = {
'running': service_running,
'status': 'online' if service_running else 'offline',
'port': port,
'users_count': len(users),
'calendars_count': len(calendars),
'events_count': len(events),
'timestamp': datetime.utcnow().isoformat()
}
return status
except Exception as e:
return self.handle_error(e, "get_status")
+20 -8
View File
@@ -33,15 +33,25 @@ class EmailManager(BaseServiceManager):
self.safe_makedirs(self.dovecot_dir)
self.safe_makedirs(os.path.dirname(self.domain_config_file))
def _get_service_config(self) -> Dict[str, Any]:
"""Read configured ports/domain from service config file."""
cfg = self.get_config()
if isinstance(cfg, dict) and 'error' not in cfg:
return cfg
return {}
def get_status(self) -> Dict[str, Any]:
"""Get email service status"""
try:
# Check if we're running in Docker environment
svc_cfg = self._get_service_config()
smtp_port = svc_cfg.get('smtp_port', 587)
imap_port = svc_cfg.get('imap_port', 993)
domain = svc_cfg.get('domain') or self._get_domain_config().get('domain', 'cell.local')
import os
is_docker = os.path.exists('/.dockerenv') or os.environ.get('DOCKER_CONTAINER') == 'true'
if is_docker:
# Check if email container is actually running
container_running = self._check_email_container_status()
status = {
'running': container_running,
@@ -49,24 +59,26 @@ class EmailManager(BaseServiceManager):
'smtp_running': container_running,
'imap_running': container_running,
'users_count': 0,
'domain': 'cell.local',
'domain': domain,
'smtp_port': smtp_port,
'imap_port': imap_port,
'timestamp': datetime.utcnow().isoformat()
}
else:
# Check actual service status in production
smtp_running = self._check_smtp_status()
imap_running = self._check_imap_status()
status = {
'running': smtp_running and imap_running,
'status': 'online' if (smtp_running and imap_running) else 'offline',
'smtp_running': smtp_running,
'imap_running': imap_running,
'users_count': len(self._load_users()),
'domain': self._get_domain_config().get('domain', 'unknown'),
'domain': domain,
'smtp_port': smtp_port,
'imap_port': imap_port,
'timestamp': datetime.utcnow().isoformat()
}
return status
except Exception as e:
return self.handle_error(e, "get_status")
+4 -1
View File
@@ -481,13 +481,16 @@ umask = 022
import os
is_docker = os.path.exists('/.dockerenv') or os.environ.get('DOCKER_CONTAINER') == 'true'
svc_cfg = self.get_config()
configured_port = svc_cfg.get('port', 80) if isinstance(svc_cfg, dict) and 'error' not in svc_cfg else 80
if is_docker:
# Check if file container is actually running
container_running = self._check_file_container_status()
status = {
'running': container_running,
'status': 'online' if container_running else 'offline',
'webdav_status': {'running': container_running, 'port': 8080},
'webdav_status': {'running': container_running, 'port': configured_port},
'users_count': 0,
'total_storage_used': {'bytes': 0, 'human_readable': '0 B'},
'timestamp': datetime.utcnow().isoformat()