From e4a17efd971768b3633a882cc57751821115cea7 Mon Sep 17 00:00:00 2001 From: Julian Cuni Date: Sat, 27 Jun 2026 17:51:32 +0200 Subject: [PATCH] feat(setup): reveal toggle for secret fields (the device web password) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- apps/web/src/SetupWizard.tsx | 35 ++++++++++++++++++++++++++++++++++- apps/web/src/lib/i18n/en.ts | 3 +++ apps/web/src/lib/i18n/sq.ts | 3 +++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/apps/web/src/SetupWizard.tsx b/apps/web/src/SetupWizard.tsx index 4249a57..05e5840 100644 --- a/apps/web/src/SetupWizard.tsx +++ b/apps/web/src/SetupWizard.tsx @@ -387,6 +387,13 @@ function DeviceForm({ const [printResult, setPrintResult] = useState(null); const [printTesting, setPrintTesting] = useState(false); const [printError, setPrintError] = useState(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>({}); const [saving, setSaving] = useState(false); const [saveError, setSaveError] = useState(null); const [found, setFound] = useState(null); @@ -688,10 +695,36 @@ function DeviceForm({ ))} + ) : 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. +
+ { + const v = e.target.value; + setConfig((c) => ({ ...c, [f.key]: v })); + resetStatus(); + }} + /> + +
) : ( { diff --git a/apps/web/src/lib/i18n/en.ts b/apps/web/src/lib/i18n/en.ts index e8bbf29..95f511a 100644 --- a/apps/web/src/lib/i18n/en.ts +++ b/apps/web/src/lib/i18n/en.ts @@ -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.", diff --git a/apps/web/src/lib/i18n/sq.ts b/apps/web/src/lib/i18n/sq.ts index 8e2d02c..acff006 100644 --- a/apps/web/src/lib/i18n/sq.ts +++ b/apps/web/src/lib/i18n/sq.ts @@ -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: