feat(setup): reveal toggle for secret fields (the device web password)
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:
@@ -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) => {
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user