fix: surface DDNS registration failure during setup wizard
Unit Tests / test (push) Successful in 7m34s
Unit Tests / test (push) Successful in 7m34s
Two problems on fresh install with pic_ngo mode: 1. Caddy crashed at startup because ddns.token was empty (registration hadn't completed yet), producing a bare `token` keyword in the Caddyfile that Caddy rejects with "wrong argument count". Fix: fall back to lan mode in _caddyfile_pic_ngo when the token is empty so Caddy always starts cleanly. The Caddyfile is regenerated once registration completes and the token is persisted. 2. DDNS registration failures were silently swallowed — the wizard showed "Setup complete!" with no indication that HTTPS wouldn't work. This made it look like everything was fine when the subdomain was never registered (e.g. name already taken from a previous install, or transient network error). Fix: capture the exception, classify it (name_taken vs transient), and return it as a `warnings` list in the setup response. The wizard done screen now shows amber warning cards with actionable text instead of auto-redirecting, giving the user a "Continue to login" button and a clear explanation of what went wrong. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -540,8 +540,9 @@ function Step4Review({ domainType, domainName, timezone, onBack, onSubmit, submi
|
||||
|
||||
export default function Setup() {
|
||||
const navigate = useNavigate();
|
||||
const [step, setStep] = useState(1);
|
||||
const [done, setDone] = useState(false);
|
||||
const [step, setStep] = useState(1);
|
||||
const [done, setDone] = useState(false);
|
||||
const [setupWarnings, setSetupWarnings] = useState([]);
|
||||
|
||||
const [password, setPassword] = useState('');
|
||||
const [passwordConfirm, setPasswordConfirm] = useState('');
|
||||
@@ -600,9 +601,13 @@ export default function Setup() {
|
||||
};
|
||||
|
||||
try {
|
||||
await setupAPI.complete(payload);
|
||||
const res = await setupAPI.complete(payload);
|
||||
const warnings = res?.data?.warnings || [];
|
||||
setSetupWarnings(warnings);
|
||||
setDone(true);
|
||||
setTimeout(() => navigate('/login', { replace: true }), 2000);
|
||||
if (warnings.length === 0) {
|
||||
setTimeout(() => navigate('/login', { replace: true }), 2000);
|
||||
}
|
||||
} catch (e) {
|
||||
setSubmitError(
|
||||
e?.response?.data?.errors?.join(' ') ||
|
||||
@@ -617,10 +622,27 @@ export default function Setup() {
|
||||
if (done) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen bg-gray-950">
|
||||
<div className="text-center">
|
||||
<div className="text-center max-w-md px-4">
|
||||
<CheckCircle className="h-12 w-12 text-green-400 mx-auto mb-4" />
|
||||
<h2 className="text-lg font-semibold text-white mb-2">Setup complete!</h2>
|
||||
<p className="text-sm text-gray-400">Redirecting to login...</p>
|
||||
{setupWarnings.length > 0 ? (
|
||||
<div className="mt-4 text-left space-y-3">
|
||||
{setupWarnings.map((w, i) => (
|
||||
<div key={i} className="flex gap-2 bg-amber-900/40 border border-amber-700 rounded-lg p-3">
|
||||
<AlertCircle className="h-4 w-4 text-amber-400 mt-0.5 shrink-0" />
|
||||
<p className="text-sm text-amber-200">{w}</p>
|
||||
</div>
|
||||
))}
|
||||
<button
|
||||
onClick={() => navigate('/login', { replace: true })}
|
||||
className="mt-4 w-full py-2 px-4 bg-primary-600 hover:bg-primary-700 text-white rounded-lg text-sm font-medium"
|
||||
>
|
||||
Continue to login
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-gray-400">Redirecting to login...</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user