Skip to content

fix: widget key timing, notification error handling, SignalR status, concurrent push#210

Merged
joszz merged 1 commit into
mainfrom
fix/medium-priority-review-items
Jul 3, 2026
Merged

fix: widget key timing, notification error handling, SignalR status, concurrent push#210
joszz merged 1 commit into
mainfrom
fix/medium-priority-review-items

Conversation

@joszz

@joszz joszz commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

Four of the six MEDIUM-priority items from an earlier full-project review (the other two, test coverage for PoiPreCachingService/PushSenderService/MqttConsumerService's merge flow, are being handled as a separate follow-up since they're a different kind of work).

  • Widget API key timing side-channel. WidgetEndpoints.cs compared the X-Widget-Key header with plain string.Equals instead of the constant-time comparison AuthEndpoints already has for the same purpose. Promoted FixedTimeEquals to internal and reused it.
  • Silent notification action failures. useNotifications.ts's archive/delete/archiveAll/deleteAll had no error handling; a failed request became an unhandled promise rejection with zero user feedback. They now catch, log to console, and set an actionError ref that NotificationPanel.vue surfaces as an inline error banner.
  • SignalR connection state never surfaced. App.vue destructured start/stop from useSignalR() but discarded the connected ref. A dropped connection left telemetry silently stale with no visible indicator. Now shows a "Live updates paused" indicator in the sidebar once a connection has been attempted and isn't currently up (new common.liveUpdatesPaused i18n key, en + nl).
  • Sequential push delivery. PushSenderService.SendToAllAsync delivered to subscribers one at a time; a single slow/unreachable push endpoint could delay delivery to everyone else. Now delivers with bounded concurrency (10 at a time) via Task.WhenAll + SemaphoreSlim, collecting dead subscriptions in a ConcurrentBag instead of a plain List.

Test plan

  • dotnet build succeeds, dotnet test passes (251/251)
  • pnpm run type-check, pnpm run lint, pnpm exec prettier --check src/ all pass
  • pnpm run test:unit --run passes (144/144 - 4 new tests added for the notification action error paths)

🤖 Generated with Claude Code

…concurrent push

Four MEDIUM-priority items from the full-project review:

- WidgetEndpoints compared the X-Widget-Key header with plain string.Equals
  instead of the constant-time comparison AuthEndpoints already uses for the
  same class of secret; promoted FixedTimeEquals to internal and reused it.
- useNotifications' archive/delete actions had no error handling at all, so
  a failed request became an unhandled promise rejection with no feedback.
  They now catch, log, and set an actionError ref that NotificationPanel
  surfaces as an inline banner.
- SignalR's connected state was tracked but never surfaced to the UI; a
  dropped connection left telemetry silently stale with no indicator
  anywhere. App.vue now shows a "Live updates paused" indicator in the
  sidebar once a connection has been attempted and is not currently up.
- PushSenderService delivered push notifications to subscribers one at a
  time; a single slow/unreachable endpoint could delay delivery to everyone
  else. Now delivers with bounded concurrency (10 at a time) via
  Task.WhenAll + SemaphoreSlim instead of a sequential foreach.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@joszz joszz force-pushed the fix/medium-priority-review-items branch from 464b358 to 2c55092 Compare July 3, 2026 06:24
@joszz joszz enabled auto-merge July 3, 2026 06:24
@joszz joszz merged commit 1e871f5 into main Jul 3, 2026
21 checks passed
@joszz joszz deleted the fix/medium-priority-review-items branch July 3, 2026 06:26
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