Skip to content

feat(portal): WeChat QR onboarding + faster/cleaner contact search#52

Merged
Germey merged 1 commit into
mainfrom
feat/portal-qr-onboarding
Jul 3, 2026
Merged

feat(portal): WeChat QR onboarding + faster/cleaner contact search#52
Germey merged 1 commit into
mainfrom
feat/portal-qr-onboarding

Conversation

@acedatacloud-dev

Copy link
Copy Markdown
Member

What

Two portal upgrades, both validated live with Playwright against the real Beijing gateway:

1. QR onboarding

When the WeChat account is signed out, the portal now shows the gateway's
login QR and continues automatically once you scan it.

  • Backend PortalService.qr() → proxies GET /api/auth/qr?type=base64, resolves
    the gateway's async UI task server-side (polls /api/tasks/{id}, bounded to
    ~10s), and hands the browser a ready-to-render data URL. 409 already logged in
    is surfaced as-is (never logs the account out).
  • Frontend: a status-gated "Connect WeChat" card (shown only when
    /api/wechat/status reports logged_in: false), with a QR image, a Refresh
    QR
    button, and a 3s login poll that reloads the view once you're in.

2. Faster, cleaner contact search (fixes real UX issues found during E2E)

The admin search fetched all ~4,600 contacts serially (gateway caps limit=200),
so the first search blocked several seconds with no feedback, and groups
leaked into the person picker
. Fixed:

  • Concurrent paging (ThreadPoolExecutor, 6 workers) + single-flight (a
    per-instance fetch lock) so overlapping searches and the background warm share
    one fetch instead of each paging thousands of rows. Cold fetch dropped from
    ~7s to ~1s; warm hits are instant.
  • type=friend filter → groups (e.g. …@chatroom) no longer pollute the
    admin allowlist search.
  • A "searching…" indicator in the dropdown, and a background cache warm on
    load so the first real search is usually instant.

Tests

tests/test_channels_portal.py gains QR cases (inline result, task-polling, 409
already-logged-in). Full suite: 412 passed, 1 skipped (the 3 Windows-only
failures are pre-existing and pass on CI Linux). ruff clean.

Playwright E2E (real gateway)

Verified: portal loads, account resolves to AceDataCloud (online), contact
search → real friends with no group leak, group checkboxes →
allowed_groups, Free-form ⇄ Require-prefix toggle, Save writes a valid
channels.toml (round-trip confirmed on disk, then restored), and the QR
onboarding card renders cleanly in light + dark mode.

🤖 对抗评审

Reviewer: Claude general-purpose subagent (Codex not installed), read-only.

VERDICT: CLEAN

Confirmed: per-instance fetch lock is created race-free under self._lock and
correctly dedupes (no redundant full fetches); _contacts_cache is always
lock-guarded; ThreadPoolExecutor is bounded (6) and closed via with; httpx.Client
is thread-safe. _fetch_all_contacts computes offsets from total and caps at
_CONTACTS_MAX (falls back when total missing); a per-page failure is swallowed
best-effort (documented tradeoff). qr() polling is bounded (~10s, no infinite
loop), surfaces 409 as PortalError(409) (not 502), and never logs the
account out. The gateway token stays server-side (never sent to the browser).
Frontend has no unhandled promise rejections and always clears the QR poll interval
before reuse. Ruff clean; Python 3.10 compatible.

@Germey Germey merged commit 2ef3cd7 into main Jul 3, 2026
3 checks passed
@Germey Germey deleted the feat/portal-qr-onboarding branch July 3, 2026 17:18
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.

2 participants