feat(setup): reveal toggle for secret fields (the device web password)
Build desktop / desktop (push) Successful in 4m37s
Build & push images / images (push) Successful in 2m52s
CI / check (push) Successful in 44s

The admin needs the device web password (to reach a controller/camera's own web
UI), and it's already stored + sent to this admin-only view (redactSecrets strips
only the machine secrets relay/push pw, NOT webPassword — by design, per the
SECRET_CONFIG_KEYS comment). But the form rendered every `secret` field as a masked
password input with no way to unmask it, so the value was present yet unreadable.

Add a per-field show/hide eye toggle on `secret` inputs. No new exposure: the field
is already admin-gated and the value already reaches the client; this just makes the
intended-visible credential readable/copyable. Machine secrets are redacted
server-side and never arrive, so there's nothing there to reveal. i18n sq+en.
This commit is contained in:
2026-06-27 17:51:32 +02:00
parent 6d32e0fc0f
commit e4a17efd97
3 changed files with 40 additions and 1 deletions
+34 -1
View File
@@ -387,6 +387,13 @@ function DeviceForm({
const [printResult, setPrintResult] = useState<PrintTestResult | null>(null);
const [printTesting, setPrintTesting] = useState(false);
const [printError, setPrintError] = useState<string | null>(null);
// Which `secret` fields are currently unmasked. The device web password is an
// operational credential the admin legitimately needs (to reach the device's web
// UI) — it's stored + sent to this admin-only view; a per-field reveal toggle just
// makes the already-present value readable. (Machine secrets — relay/push pw — are
// redacted server-side and never reach here, so there's nothing to reveal.)
const [revealed, setRevealed] = useState<Record<string, boolean>>({});
const [saving, setSaving] = useState(false);
const [saveError, setSaveError] = useState<string | null>(null);
const [found, setFound] = useState<DiscoveredDevice[] | null>(null);
@@ -688,10 +695,36 @@ function DeviceForm({
</option>
))}
</select>
) : f.type === "secret" ? (
// Secret field with a reveal toggle: the device web password is shown
// here (admin-only view) so an admin can read/copy it to reach the
// device's own web UI. Masked by default; click the eye to reveal.
<div className="flex gap-1">
<input
className="input flex-1"
type={revealed[f.key] ? "text" : "password"}
value={(config[f.key] ?? (f.default as string | number | undefined) ?? "") as string | number}
placeholder={f.help}
onChange={(e) => {
const v = e.target.value;
setConfig((c) => ({ ...c, [f.key]: v }));
resetStatus();
}}
/>
<button
type="button"
className="btn btn-sm"
aria-label={revealed[f.key] ? t("setup.hideSecret") : t("setup.revealSecret")}
title={revealed[f.key] ? t("setup.hideSecret") : t("setup.revealSecret")}
onClick={() => setRevealed((r) => ({ ...r, [f.key]: !r[f.key] }))}
>
{revealed[f.key] ? "🙈" : "👁"}
</button>
</div>
) : (
<input
className="input"
type={f.type === "secret" ? "password" : f.type === "number" || f.type === "port" ? "number" : "text"}
type={f.type === "number" || f.type === "port" ? "number" : "text"}
value={(config[f.key] ?? (f.default as string | number | undefined) ?? "") as string | number}
placeholder={f.help}
onChange={(e) => {
+3
View File
@@ -413,6 +413,9 @@ export const en: Catalog = {
"Sends a test slip to the printer now. ‘Connected’ only opens the link — this confirms the printer actually feeds paper.",
printOk: "✓ Test slip sent ({{ms}} ms). Check the printer.",
"printFail.print-failed": "The printer rejected the job (out of paper, cover open, or the link dropped).",
// Reveal/hide toggle for a secret field (e.g. the device web password).
revealSecret: "Show password",
hideSecret: "Hide password",
alarmUrlTitle: "Alarm Server settings (enter these in the camera)",
alarmUrlHint:
"Enter these in the camera at Configuration → Event → … → Alarm Settings (or Notify Surveillance Center). The camera POSTs every event here — no polling.",
+3
View File
@@ -423,6 +423,9 @@ export const sq = {
"Dërgon një fletë prove te printeri tani. ‘I lidhur’ vetëm hap lidhjen — kjo konfirmon se printeri vërtet nxjerr letër.",
printOk: "✓ Fleta e provës u dërgua ({{ms}} ms). Kontrollo printerin.",
"printFail.print-failed": "Printeri nuk pranoi punën (pa letër, kapaku hapur, ose lidhja ra).",
// Reveal/hide toggle for a secret field (e.g. the device web password).
revealSecret: "Shfaq fjalëkalimin",
hideSecret: "Fshih fjalëkalimin",
// Alarm Server push settings — generated for the camera's Event → Alarm Server form.
alarmUrlTitle: "Cilësimet e Alarm Server (vendosi te kamera)",
alarmUrlHint: