Fix DDNS security and reliability gaps (#2, #3, #5, #6, #7)
Unit Tests / test (push) Successful in 7m23s
Unit Tests / test (push) Successful in 7m23s
- Fix #2: Move DDNS bearer token from cell_config.json to data/api/ddns_token. Token is now in the secrets store (data/) rather than the config store (config/). Auto-migrates existing installs on first access. ConfigManager.get/set_ddns_token() added. set_ddns_config() now strips 'token' key to prevent it leaking back. - Fix #3: Set Caddyfile permissions to 0o600 after write so the token embedded in the Caddyfile is not world-readable on the host filesystem. - Fix #5: Heartbeat now fires IDENTITY_CHANGED after re-registration so Caddy regenerates its config with the new token automatically — users no longer need to click Re-register in Settings after a wizard registration failure. Also: heartbeat skips the 401-cycle when no token exists and goes straight to registration instead. DDNSManager now accepts service_bus= and is wired up. - Fix #6: Settings page starts polling GET /api/caddy/cert-status every 15s after a successful DDNS re-registration and shows "Acquiring certificate…" feedback until Let's Encrypt issues the cert (up to 5 minutes). - Fix #7: regenerate_with_installed() is debounced (5 s window) so two rapid IDENTITY_CHANGED events (e.g. wizard + heartbeat) can't start simultaneous ACME orders that interfere with each other. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -123,10 +123,15 @@ def get_config():
|
||||
config['domain_name'] = identity.get('domain_name', '')
|
||||
config['effective_domain'] = config_manager.get_effective_domain()
|
||||
ddns_section = config_manager.configs.get('ddns', {})
|
||||
_provider = ddns_section.get('provider', '')
|
||||
_has_token = bool(
|
||||
(config_manager.get_ddns_token() if _provider == 'pic_ngo' else '') or
|
||||
ddns_section.get('api_token') or ddns_section.get('token')
|
||||
)
|
||||
config['ddns'] = {
|
||||
'provider': ddns_section.get('provider', ''),
|
||||
'provider': _provider,
|
||||
'subdomain': ddns_section.get('subdomain', ''),
|
||||
'has_token': bool(ddns_section.get('token') or ddns_section.get('api_token')),
|
||||
'has_token': _has_token,
|
||||
}
|
||||
return jsonify(config)
|
||||
except Exception as e:
|
||||
@@ -613,7 +618,7 @@ def ddns_status():
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
registered = bool(ddns_cfg.get('token'))
|
||||
registered = bool(config_manager.get_ddns_token())
|
||||
return jsonify({
|
||||
'registered': registered,
|
||||
'domain_name': identity.get('domain_name', ''),
|
||||
|
||||
Reference in New Issue
Block a user