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:
+21
-7
@@ -403,15 +403,29 @@ def update_config():
|
|||||||
config_manager.configs['_identity'] = stored
|
config_manager.configs['_identity'] = stored
|
||||||
config_manager._save_all_configs()
|
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():
|
for service, config in data.items():
|
||||||
if service in config_manager.service_schemas:
|
if service in config_manager.service_schemas:
|
||||||
success = config_manager.update_service_config(service, config)
|
config_manager.update_service_config(service, config)
|
||||||
if success:
|
# Propagate to the service manager's own config file
|
||||||
service_bus.publish_event(EventType.CONFIG_CHANGED, service, {
|
mgr = _svc_managers.get(service)
|
||||||
'service': service,
|
if mgr:
|
||||||
'config': config
|
mgr.update_config(config)
|
||||||
})
|
service_bus.publish_event(EventType.CONFIG_CHANGED, service, {
|
||||||
|
'service': service,
|
||||||
|
'config': config
|
||||||
|
})
|
||||||
|
|
||||||
logger.info(f"Updated config: {data}")
|
logger.info(f"Updated config: {data}")
|
||||||
return jsonify({"message": "Configuration updated successfully"})
|
return jsonify({"message": "Configuration updated successfully"})
|
||||||
|
|||||||
@@ -29,9 +29,16 @@ class CalendarManager(BaseServiceManager):
|
|||||||
self.safe_makedirs(self.calendar_data_dir)
|
self.safe_makedirs(self.calendar_data_dir)
|
||||||
self.safe_makedirs(self.radicale_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]:
|
def get_status(self) -> Dict[str, Any]:
|
||||||
"""Get calendar service status"""
|
"""Get calendar service status"""
|
||||||
try:
|
try:
|
||||||
|
port = self._get_configured_port()
|
||||||
# Check if we're running in Docker environment
|
# Check if we're running in Docker environment
|
||||||
import os
|
import os
|
||||||
is_docker = os.path.exists('/.dockerenv') or os.environ.get('DOCKER_CONTAINER') == 'true'
|
is_docker = os.path.exists('/.dockerenv') or os.environ.get('DOCKER_CONTAINER') == 'true'
|
||||||
@@ -42,6 +49,7 @@ class CalendarManager(BaseServiceManager):
|
|||||||
status = {
|
status = {
|
||||||
'running': container_running,
|
'running': container_running,
|
||||||
'status': 'online' if container_running else 'offline',
|
'status': 'online' if container_running else 'offline',
|
||||||
|
'port': port,
|
||||||
'users_count': 0,
|
'users_count': 0,
|
||||||
'calendars_count': 0,
|
'calendars_count': 0,
|
||||||
'events_count': 0,
|
'events_count': 0,
|
||||||
@@ -57,6 +65,7 @@ class CalendarManager(BaseServiceManager):
|
|||||||
status = {
|
status = {
|
||||||
'running': service_running,
|
'running': service_running,
|
||||||
'status': 'online' if service_running else 'offline',
|
'status': 'online' if service_running else 'offline',
|
||||||
|
'port': port,
|
||||||
'users_count': len(users),
|
'users_count': len(users),
|
||||||
'calendars_count': len(calendars),
|
'calendars_count': len(calendars),
|
||||||
'events_count': len(events),
|
'events_count': len(events),
|
||||||
|
|||||||
+18
-6
@@ -33,15 +33,25 @@ class EmailManager(BaseServiceManager):
|
|||||||
self.safe_makedirs(self.dovecot_dir)
|
self.safe_makedirs(self.dovecot_dir)
|
||||||
self.safe_makedirs(os.path.dirname(self.domain_config_file))
|
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]:
|
def get_status(self) -> Dict[str, Any]:
|
||||||
"""Get email service status"""
|
"""Get email service status"""
|
||||||
try:
|
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
|
import os
|
||||||
is_docker = os.path.exists('/.dockerenv') or os.environ.get('DOCKER_CONTAINER') == 'true'
|
is_docker = os.path.exists('/.dockerenv') or os.environ.get('DOCKER_CONTAINER') == 'true'
|
||||||
|
|
||||||
if is_docker:
|
if is_docker:
|
||||||
# Check if email container is actually running
|
|
||||||
container_running = self._check_email_container_status()
|
container_running = self._check_email_container_status()
|
||||||
status = {
|
status = {
|
||||||
'running': container_running,
|
'running': container_running,
|
||||||
@@ -49,21 +59,23 @@ class EmailManager(BaseServiceManager):
|
|||||||
'smtp_running': container_running,
|
'smtp_running': container_running,
|
||||||
'imap_running': container_running,
|
'imap_running': container_running,
|
||||||
'users_count': 0,
|
'users_count': 0,
|
||||||
'domain': 'cell.local',
|
'domain': domain,
|
||||||
|
'smtp_port': smtp_port,
|
||||||
|
'imap_port': imap_port,
|
||||||
'timestamp': datetime.utcnow().isoformat()
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
# Check actual service status in production
|
|
||||||
smtp_running = self._check_smtp_status()
|
smtp_running = self._check_smtp_status()
|
||||||
imap_running = self._check_imap_status()
|
imap_running = self._check_imap_status()
|
||||||
|
|
||||||
status = {
|
status = {
|
||||||
'running': smtp_running and imap_running,
|
'running': smtp_running and imap_running,
|
||||||
'status': 'online' if (smtp_running and imap_running) else 'offline',
|
'status': 'online' if (smtp_running and imap_running) else 'offline',
|
||||||
'smtp_running': smtp_running,
|
'smtp_running': smtp_running,
|
||||||
'imap_running': imap_running,
|
'imap_running': imap_running,
|
||||||
'users_count': len(self._load_users()),
|
'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()
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -481,13 +481,16 @@ umask = 022
|
|||||||
import os
|
import os
|
||||||
is_docker = os.path.exists('/.dockerenv') or os.environ.get('DOCKER_CONTAINER') == 'true'
|
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:
|
if is_docker:
|
||||||
# Check if file container is actually running
|
# Check if file container is actually running
|
||||||
container_running = self._check_file_container_status()
|
container_running = self._check_file_container_status()
|
||||||
status = {
|
status = {
|
||||||
'running': container_running,
|
'running': container_running,
|
||||||
'status': 'online' if container_running else 'offline',
|
'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,
|
'users_count': 0,
|
||||||
'total_storage_used': {'bytes': 0, 'human_readable': '0 B'},
|
'total_storage_used': {'bytes': 0, 'human_readable': '0 B'},
|
||||||
'timestamp': datetime.utcnow().isoformat()
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ function Email() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-50 rounded-lg px-4 py-2">
|
<div className="bg-gray-50 rounded-lg px-4 py-2">
|
||||||
<InfoRow label="Server" value={CELL_HOST} />
|
<InfoRow label="Server" value={CELL_HOST} />
|
||||||
<InfoRow label="Port" value="993" />
|
<InfoRow label="Port" value={String(status?.imap_port ?? 993)} />
|
||||||
<InfoRow label="Security" value="SSL/TLS" />
|
<InfoRow label="Security" value="SSL/TLS" />
|
||||||
<InfoRow label="Direct IP" value={CELL_IP} />
|
<InfoRow label="Direct IP" value={CELL_IP} />
|
||||||
</div>
|
</div>
|
||||||
@@ -93,7 +93,7 @@ function Email() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-50 rounded-lg px-4 py-2">
|
<div className="bg-gray-50 rounded-lg px-4 py-2">
|
||||||
<InfoRow label="Server" value={CELL_HOST} />
|
<InfoRow label="Server" value={CELL_HOST} />
|
||||||
<InfoRow label="Port" value="587" />
|
<InfoRow label="Port" value={String(status?.smtp_port ?? 587)} />
|
||||||
<InfoRow label="Security" value="STARTTLS" />
|
<InfoRow label="Security" value="STARTTLS" />
|
||||||
<InfoRow label="Auth" value="Username + Password" />
|
<InfoRow label="Auth" value="Username + Password" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -495,7 +495,8 @@ function Settings() {
|
|||||||
return (
|
return (
|
||||||
<Section key={key} icon={Icon} title={label} collapsible defaultOpen={false}>
|
<Section key={key} icon={Icon} title={label} collapsible defaultOpen={false}>
|
||||||
<Form data={data} onChange={(d) => updateServiceConfig(key, d)} />
|
<Form data={data} onChange={(d) => updateServiceConfig(key, d)} />
|
||||||
<div className="flex justify-end mt-4">
|
<div className="flex items-center justify-between mt-4">
|
||||||
|
<span className="text-xs text-gray-400">Port/directory changes take effect after container restart.</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => saveService(key)}
|
onClick={() => saveService(key)}
|
||||||
disabled={!dirty || saving}
|
disabled={!dirty || saving}
|
||||||
|
|||||||
Reference in New Issue
Block a user