Skip to content

Feat/batch auth ux#67

Open
shiliu-yang wants to merge 44 commits into
refactor/v3from
feat/batch-auth-ux
Open

Feat/batch auth ux#67
shiliu-yang wants to merge 44 commits into
refactor/v3from
feat/batch-auth-ux

Conversation

@shiliu-yang

Copy link
Copy Markdown
Contributor

No description provided.

Replace inner-list scroll with outer page scroll so the scrollbar
spans the full content area. Dashboard is pinned sticky at top so
cumulative stats remain visible while scrolling through slot rows.

Introduces a new 'fullBleedScroll' AppShell layout variant: full-width
like fullBleed but with overflow-y:auto on the main container instead
of overflow:hidden, enabling sticky positioning to work correctly.
Status badge now shows a phase-aware label during flashing instead of
a flat "烧录中" for all sub-phases:
- handshake/connect/switch_baud/read_flash_id/load_ram → 连接中
- erase → 擦除中
- write/write_segment → 写入中
- verify → 校验中

Also fixes write_segment phase being stringified as "[object Object]":
normalizePhase() in the store extracts the variant key from compound
FlashPhase objects before storing currentPhase.

i18n: add status.{connecting,erasing,writing,verifying} and
phase.{handshake,connect,switch_baud,read_flash_id,load_ram,erase,
write,write_segment,verify,protect,unprotect,reboot} to both locales.
…w, trim i18n copy

- BatchAuthConfig: conflict policy and T5AI storage mode now share a single flex-wrap
  row instead of two separate rows; wraps gracefully on narrow viewports
- AppShell nav: allow label text to wrap on md+ screens to avoid truncation
- i18n: remove "(Recommended)" tag from storageKv; shorten storageOtpWarning to be
  more concise in both en and zh-CN
- Add BatchSharedConfig interface and SHARED_CONFIG_KEY to workspace
- loadBatchFlashAuthWorkspace now returns sharedConfig
- loadPersistedData restores chip/baudRate/authBaudRate; first run falls
  back to chip manifest defaults (e.g. esp32→460800, t5ai→921600)
- Watch [chipId, baudRate, authBaudRate] to auto-save on any change
…de selector right

- auth_read() now accepts AuthStorage; OTP sends "auth-read 1", KV sends
  "auth-read" (no param) to stay compatible with older firmware
- auth_write already had the same pattern for the write side; read side
  now matches
- Add two unit tests: auth_read_kv_omits_storage_param and
  auth_read_otp_appends_storage_param
- UI: push "写入位置" group to the right via ml-auto
Add i18n keys for read operations:
- toolbar: readAll, readAllHint, readAllBusy
- slot: read, readHint, authorized, notAuthorized, readError
- status: reading (already existed)

Signed-off-by: YangJie <huate.lai@tuya.com>
…tch_auth_start

Previously the Done arm emitted the event only inside `if let Some(idx) = row_idx`,
so if a Done result arrived without an allocated row (invariant violation) the
frontend would receive no event and the slot would be stuck. Restructure so
confirm_row is still guarded by row_idx but the emit always fires.
When a read probe fails, the previous successful probe's mac/authUuid/isAuthorized
were left in the slot state, causing readError to be silently hidden by the
`v-if="portSlot.readError && !portSlot.mac"` guard. Fix both sides:
- Store: clear mac/authUuid/isAuthorized when step==="failed" in handleReadProgress
- Template: simplify condition to `v-if="portSlot.readError"` (mac is always
  undefined when readError is set, so the old guard was redundant and harmful)
…rored

canStart previously allowed `excelStats === null`, which lets the user
press Start while validateExcel is still resolving or has surfaced an
error — the click then drops straight into a Rust-side allocator load
failure. Tighten canStart to require non-null stats with remaining > 0
and no excelError, surface the invalid-table reason in the Start button
title, and cover the new gating in unit tests.
eFuse burning may take significantly longer than a normal shell
command; use AUTH_OTP_LOCK_TIMEOUT=2s and AUTH_OTP_LOCK_IDLE=500ms
instead of the 50ms cmd_idle_timeout to avoid premature termination
that would leave the device in an indeterminate locked-or-not state.
Adds lock_otp parameter and BatchAuthSlotResult::LockFailed variant.
When lock_otp is true and verify succeeds, send auth-otp-lock; on
failure return LockFailed (NOT Err) so callers can confirm the Excel
row instead of releasing the UUID for another device.
Adds backend-side validation (chipId=t5ai && storage=otp) so a
malicious or buggy client cannot trigger auth-otp-lock in KV mode.
LockFailed slots confirm_row instead of releasing, preventing UUID
reuse on a device whose OTP region has been written but not locked.
Mirrors the Rust BatchAuthStartConfig.lock_otp_after_auth field.
Store and IPC call site wiring follows in subsequent commits.
- Default false; loadPersistedData explicitly overwrites to false on
  every app start (never trust the persisted value for an irreversible
  hardware operation).
- watch resets the toggle when chipId or authStorage leaves the
  (t5ai, otp) combination, preventing a hidden checked state.
- startAuth passes the field through to batch_auth_start IPC.
- handleAuthProgress failed branch now carries excelError and mac for
  LockFailed payloads.
Copy emphasises irreversibility ("不可恢复" / "cannot be undone") and
that the toggle resets on every app start, so the operator must
re-acknowledge the danger each session.
Red-bordered checkbox below the existing orange OTP warning. Visible
only when chipId=t5ai AND authStorage=otp, matching the visibility
of the existing storageOtpWarning. Disabled while a batch is busy.
Copy emphasises irreversibility and the auto-reset-on-restart policy.
Finding 1: add LockFailed caller contract to run_batch_auth_slot doc-comment.
Finding 2: degrade hardware_reset failure after a successful lock+Done from
  a propagated Err (which would cause release_row and UUID reuse) to a warn
  log; eFuse is already burned so confirming the Excel row is correct.
Finding 4: add test documenting mixed success+failure response semantics for
  auth_otp_lock — success line wins over failure line.
When auth was written to OTP but eFuse lock subsequently failed, the
operator must physically isolate the device. Previously the slot showed
the same 'failed' appearance as any other error, providing no signal.

- Tauri bridge emits lockFailed:true in the batch-auth-progress payload
  for LockFailed results only
- BatchSlotState and BatchAuthProgressEvent gain a lockFailed?: boolean
  field with JSDoc explaining the isolation requirement
- handleAuthProgress propagates lockFailed through to slot state
- BatchFlashAuthSlotRow renders an inline danger-colored warning label
  under the error message when slot.lockFailed === true
- i18n: add batchFlashAuth.slot.lockFailedLabel to both zh-CN and en
- Tests: two new cases in batch-flash-auth.test.ts verify that lockFailed
  is set on LockFailed payloads and absent on normal failed payloads
canRetry and retryFailed now skip slots where lockFailed=true, as those
devices had auth written to OTP but eFuse lock subsequently failed and
must be physically isolated — retrying would silently skip the lock step.

Adds two tests:
- retryFailed does not reset lockFailed slots
- canRetry is false when only lockFailed slots remain
- Thicker left border (4px vs 3px) and deeper danger background (12% vs 6%)
  for lockFailed slots to make them stand out from ordinary failures
- Warning triangle badge (⚠) in top-right corner of lockFailed slot rows
- Status badge text uses new lockFailedBadge i18n key ("锁失败"/"Lock failed")
  instead of the generic "failed" label
- Retry button hidden for lockFailed slots (consistent with F-Audit-1)
- Adds lockFailedBadge and emptyHintNoPorts i18n keys (zh-CN + en)
validate_excel_file now returns 'excel.fileNotFound' and
'excel.notXlsxFormat' instead of hardcoded Chinese strings.
The store maps these codes to i18n keys before storing in excelError.
BatchAuthConfig.vue translates the key via te()/t() with raw-string
passthrough for unknown errors.

Adds i18n keys:
- batchFlashAuth.errors.excel.fileNotFound (zh/en)
- batchFlashAuth.errors.excel.notXlsxFormat (zh/en)
… fix empty hint

F-Audit-4: Dashboard elapsed timer now freezes when the batch completes.
  checkBatchCompletion records batchEndTime; the interval uses
  batchEndTime ?? Date.now() so the display stops accumulating.
  startAuth / retryPort reset batchEndTime=null to resume timing on the
  next run. batchEndTime is exposed from the store.

F-Audit-5: BatchFlashAuthSlotList onRetry now calls store.retryPort(port)
  instead of manually resetting the slot and calling startBatch(). The
  new retryPort action resets and re-starts only that single port, skips
  lockFailed slots, and resets batchEndTime to resume the elapsed timer.

F-Audit-6: Empty-state hint in SlotList now uses new key
  batchFlashAuth.slot.emptyHintNoPorts ("未检测到串口;请插入设备后
  点击自动分配" / "No serial ports detected; insert a device and
  click Auto Assign") which is accurate when no ports have been added.
…e race

When run_batch_auth_slot is cancelled after auth_write returns but before
verify completes, return CancelledAfterWrite{mac, uuid} instead of plain
Cancelled. All post-auth_write check_cancel!() calls are replaced with
explicit cancel.load() checks that return the new variant; pre-write cancel
points keep returning Cancelled unchanged.

Covers both new-firmware and old-firmware branches. The new variant signals
to the caller that the UUID must be confirmed (not released) to prevent the
same credential from being handed out to a second device.
Add a new match arm for BatchAuthSlotResult::CancelledAfterWrite in
batch_auth_start. Calls confirm_row(idx, mac) to mark the Excel row as
Used (preventing UUID reuse), then emits a "cancelled_after_write" progress
event with port, mac, and uuid fields for the frontend.

This fixes the OTP-brick race: previously Cancelled always called release_row,
which returned the UUID to the available pool; with OTP mode the second device
receiving that UUID would hit hardware rejection.
…antine-required

- types.ts: add "cancelled_after_write" to BatchAuthProgressEvent.step union,
  uuid? field, and cancelledAfterWrite? boolean to BatchSlotState
- store: handle cancelled_after_write step → status=failed, cancelledAfterWrite=true,
  fail stats incremented; canRetry/retryFailed/retryPort all exclude these slots
- SlotRow.vue: thick danger border + ⚠ badge for cancelledAfterWrite (same visual
  treatment as lockFailed); distinct i18n badge text; retry button hidden
- i18n: add cancelledAfterWriteBadge / Label / Error keys to zh-CN + en
- tests: 4 new tests covering the event handler, canRetry, retryFailed, retryPort
Chrome DevTools UI verification revealed that the w-24 (96px) status
column wraps "Cancel after write" and "Insufficient Codes" badges
across two lines in English mode, breaking the table's row rhythm.
w-28 (112px) + leading-tight fits all single-line in both languages
and keeps row heights consistent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant