feat: make DDNS domain_name the effective domain across all services
Unit Tests / test (push) Successful in 11m35s
Unit Tests / test (push) Successful in 11m35s
- ConfigManager.get_effective_domain(): returns domain_name when DDNS
active (pic_ngo/cloudflare/duckdns), domain otherwise. Used by all
public-facing services so they use the real registered FQDN.
- ConfigManager.get_internal_domain(): always returns _identity.domain
(CoreDNS zone name, dnsmasq, cell-link invites — stays internal).
- Silent migration: if domain_mode != lan and domain is generic "cell",
auto-set to {cell_name}.local for unique CoreDNS zone naming.
- caddy_manager: fix custom_domain bug — cloudflare/http01 modes were
reading identity.get('custom_domain') which never exists; now reads
domain_name correctly.
- routes/config, app: expose effective_domain in GET /api/config and
/api/status responses.
- email_manager, routes/email: use get_effective_domain() for
OVERRIDE_HOSTNAME, POSTMASTER_ADDRESS, and new-user email defaults.
- ServiceBus.IDENTITY_CHANGED event: emitted from PUT /api/config and
POST /api/ddns/register after identity writes; caddy_manager and
email_manager subscribe to regenerate config automatically.
- Settings.jsx: hide Local Domain input in non-LAN modes; show
read-only effective_domain with "managed by DDNS" badge and an
Advanced toggle for the internal CoreDNS zone name.
- 11 new test classes covering all new helpers, event subscriptions,
caddy/email handlers, and the custom_domain fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+15
-1
@@ -19,7 +19,8 @@ logger = logging.getLogger(__name__)
|
||||
class EmailManager(BaseServiceManager):
|
||||
"""Manages email service configuration and users"""
|
||||
|
||||
def __init__(self, data_dir: str = '/app/data', config_dir: str = '/app/config'):
|
||||
def __init__(self, data_dir: str = '/app/data', config_dir: str = '/app/config',
|
||||
service_bus=None):
|
||||
super().__init__('email', data_dir, config_dir)
|
||||
self.email_data_dir = os.path.join(data_dir, 'email')
|
||||
self.email_dir = self.email_data_dir # alias used by tests
|
||||
@@ -33,6 +34,10 @@ class EmailManager(BaseServiceManager):
|
||||
self.safe_makedirs(self.dovecot_dir)
|
||||
self.safe_makedirs(os.path.dirname(self.domain_config_file))
|
||||
|
||||
if service_bus is not None:
|
||||
from service_bus import EventType
|
||||
service_bus.subscribe_to_event(EventType.IDENTITY_CHANGED, self._on_identity_changed)
|
||||
|
||||
def _get_service_config(self) -> Dict[str, Any]:
|
||||
"""Read configured ports/domain from service config file."""
|
||||
cfg = self.get_config()
|
||||
@@ -252,6 +257,15 @@ class EmailManager(BaseServiceManager):
|
||||
|
||||
return {'restarted': restarted, 'warnings': warnings}
|
||||
|
||||
def _on_identity_changed(self, event) -> None:
|
||||
"""Regenerate email config when cell identity changes."""
|
||||
try:
|
||||
effective = event.data.get('effective_domain')
|
||||
if effective:
|
||||
self.apply_config({'domain': effective})
|
||||
except Exception as exc:
|
||||
self.logger.warning('email_manager identity_changed handler failed: %s', exc)
|
||||
|
||||
def get_email_status(self) -> Dict[str, Any]:
|
||||
"""Get detailed email service status including postfix/dovecot state."""
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user