fix(cabinet): recover "Сервис недоступен" false-positive + top-up fixes

- health probe: tolerant timeout (12s) + retry before flagging the backend down. A
  hardcoded 5s probe racing auth bootstrap falsely showed ServiceUnavailableScreen on
  slow devices / cold mobile connections while the 30s API requests would have
  succeeded ("works on one device, not another on the same Wi-Fi"). The recovery poll
  self-reschedules with the tolerant timeout so slow devices auto-recover.
- TopUpAmount: fetch payment methods with a real query (fixes the infinite spinner on a
  cold cache / browser-back) and use canonical RUB for quick-amount chips so FX rounding
  can't reject a min-amount selection in non-RUB locales.
- settings UI: render secret values as a masked password input (pairs with the backend
  secret masking); leaving the field empty keeps the stored secret.
- deps: npm audit fix (18 -> 5 advisories).

Also bundles in-progress settings env-lock UI work.
This commit is contained in:
c0mrade
2026-06-10 16:15:55 +03:00
parent 75a570b862
commit 16fad9f4fe
14 changed files with 391 additions and 227 deletions

View File

@@ -37,6 +37,8 @@ export function SettingsTableRow({
const isModified = setting.has_override;
const isBool = setting.type === 'bool';
const boolChecked = setting.current === true || setting.current === 'true';
// env-locked keys are pinned in .env and shadow the DB — show value, no input.
const locked = setting.read_only || setting.env_locked;
const isLongValue = (() => {
const val = String(setting.current ?? '');
@@ -84,14 +86,17 @@ export function SettingsTableRow({
</span>
)}
{setting.has_override && !setting.read_only && (
{setting.has_override && !locked && (
<span className="rounded-full bg-sky-500/20 px-1.5 py-0.5 text-[10px] font-medium leading-none text-sky-400">
{t('admin.settings.badgeDb')}
</span>
)}
{setting.read_only && (
<span className="flex items-center gap-0.5 rounded-full bg-warning-500/15 px-1.5 py-0.5 text-[10px] font-medium leading-none text-warning-400">
{setting.env_locked && (
<span
className="flex items-center gap-0.5 rounded-full bg-warning-500/15 px-1.5 py-0.5 text-[10px] font-medium leading-none text-warning-400"
title={t('admin.settings.envLockedHint')}
>
{t('admin.settings.badgeEnv')}
<LockIcon className="h-3 w-3" />
</span>
@@ -116,7 +121,7 @@ export function SettingsTableRow({
isLongValue ? 'w-full' : 'max-lg:self-end lg:flex-shrink-0',
)}
>
{setting.read_only ? (
{locked ? (
<span className="max-w-[240px] truncate rounded bg-dark-700/30 px-3 py-1.5 font-mono text-xs text-dark-400">
{isBool
? boolChecked
@@ -138,7 +143,7 @@ export function SettingsTableRow({
)}
{/* Reset button -- hover-reveal when has_override */}
{isModified && !setting.read_only && (
{isModified && !locked && (
<button
onClick={onReset}
disabled={isResetting}