feat: make DDNS domain_name the effective domain across all services
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:
2026-05-28 02:48:47 -04:00
parent 393d56d4ca
commit 1f016de855
13 changed files with 403 additions and 25 deletions
+34 -1
View File
@@ -214,5 +214,38 @@ class TestServiceBus(unittest.TestCase):
mock_service.stop.assert_called_once()
mock_service.start.assert_called_once()
class TestIdentityChangedEventType(unittest.TestCase):
"""Tests for the IDENTITY_CHANGED event type."""
def test_identity_changed_event_type_exists(self):
self.assertEqual(EventType.IDENTITY_CHANGED.value, "identity_changed")
def test_identity_changed_published_and_received(self):
"""Publish IDENTITY_CHANGED and verify the subscriber receives it."""
bus = ServiceBus()
bus.start()
try:
received = []
def handler(event):
received.append(event)
bus.subscribe_to_event(EventType.IDENTITY_CHANGED, handler)
bus.publish_event(EventType.IDENTITY_CHANGED, 'test', {
'cell_name': 'mycell',
'domain': 'cell',
'domain_name': 'mycell.pic.ngo',
'domain_mode': 'pic_ngo',
'effective_domain': 'mycell.pic.ngo',
})
time.sleep(0.2)
self.assertEqual(len(received), 1)
self.assertEqual(received[0].event_type, EventType.IDENTITY_CHANGED)
self.assertEqual(received[0].data['cell_name'], 'mycell')
self.assertEqual(received[0].data['effective_domain'], 'mycell.pic.ngo')
finally:
bus.stop()
if __name__ == '__main__':
unittest.main()
unittest.main()