feat: DDNS self-healing heartbeat + manual re-register endpoint
Unit Tests / test (push) Successful in 15m26s
Unit Tests / test (push) Successful in 15m26s
- DDNSTokenExpired exception triggers auto re-register in update_ip() so cells recover silently after a DDNS DB reset - POST /api/ddns/register lets the user force re-registration from Settings - Re-register button in Settings → External Domain & DDNS (pic_ngo only) - 3 new tests covering register endpoint: wrong provider, missing name, success Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -444,6 +444,7 @@ function Settings() {
|
||||
const [ddnsDuckStatus, setDdnsDuckStatus] = useState(null);
|
||||
const [ddnsDirty, setDdnsDirty] = useState(false);
|
||||
const [ddnsSaving, setDdnsSaving] = useState(false);
|
||||
const [ddnsRegistering, setDdnsRegistering] = useState(false);
|
||||
|
||||
// service configs
|
||||
const [serviceConfigs, setServiceConfigs] = useState({});
|
||||
@@ -595,6 +596,21 @@ function Settings() {
|
||||
}
|
||||
}, [ddnsCfToken, domainName]);
|
||||
|
||||
const reRegister = useCallback(async () => {
|
||||
setDdnsRegistering(true);
|
||||
try {
|
||||
const res = await ddnsAPI.register();
|
||||
setDomainName(res.data.subdomain || '');
|
||||
setDdnsHasToken(true);
|
||||
setPicAvail(null);
|
||||
toast(`Registered as ${res.data.subdomain}`);
|
||||
} catch (err) {
|
||||
toast(err.response?.data?.error || 'Registration failed', 'error');
|
||||
} finally {
|
||||
setDdnsRegistering(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const verifyDuck = useCallback(async () => {
|
||||
if (!ddnsDuckToken.trim()) return;
|
||||
setDdnsDuckStatus('checking');
|
||||
@@ -865,9 +881,18 @@ function Settings() {
|
||||
<Section icon={Globe} title="External Domain & DDNS" collapsible defaultOpen>
|
||||
<div className="space-y-3">
|
||||
{domainMode === 'pic_ngo' && (
|
||||
<div className="rounded-lg bg-blue-50 border border-blue-100 px-4 py-3 text-sm text-blue-700">
|
||||
Your cell is registered as <span className="font-mono font-semibold">{domainName || `${identity.cell_name}.pic.ngo`}</span> on pic.ngo.
|
||||
Change the Cell Name above to update this subdomain.
|
||||
<div className="space-y-2">
|
||||
<div className="rounded-lg bg-blue-50 border border-blue-100 px-4 py-3 text-sm text-blue-700">
|
||||
Your cell is registered as <span className="font-mono font-semibold">{domainName || `${identity.cell_name}.pic.ngo`}</span> on pic.ngo.
|
||||
Change the Cell Name above to update this subdomain.
|
||||
</div>
|
||||
<button
|
||||
onClick={reRegister}
|
||||
disabled={ddnsRegistering}
|
||||
className="px-3 py-1.5 text-xs font-medium rounded border border-blue-300 text-blue-700 hover:bg-blue-50 disabled:opacity-50"
|
||||
>
|
||||
{ddnsRegistering ? 'Registering…' : 'Re-register with pic.ngo'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{domainMode === 'cloudflare' && (
|
||||
|
||||
Reference in New Issue
Block a user