Require oauth state/redirect consistency, fail closed on missing proxy,
and remove password login from create/reauth UI (admin-only password path stays off by default).
Add mode-first connectivity probes for text/image/video/search/tts/stt/realtime,
standalone voice and web-search paths, media upload options, and in-browser
image/audio/video preview (including ZDR-safe b64 images and edit validation).
- Add 'composite' to imagePricingPlatforms set in groupsImagePricing.ts
- Allow Composite groups to toggle allow_image_generation in admin UI
- Backend already supports allow_image_generation field for all platforms
- Add test case to verify Composite platform is included
Fixes#5369
#5261 added a captcha check to POST /auth/oauth/pending/create-account, but
the shared create-account form only gates its send-code button on the
Turnstile token — the submit button's disabled condition never included it.
Turnstile tokens are single-use, so handleSendCode resets the widget in its
finally block and clears the token. Submitting inside the window before the
widget re-solves omits turnstile_token from the payload, and the backend
answers ErrTurnstileVerificationFailed (turnstile_service.go:65). With an
interaction-required challenge the widget does not re-solve on its own, so
the failure persists until the user notices and verifies again — while the
button stays enabled and says nothing.
Gate the submit button on the token, mirroring the send-code button and
EmailVerifyView, which already gates its pending-OAuth submit the same way.
handleSubmit repeats the check because implicit form submission (Enter in a
text input) bypasses the button's disabled state.
The Tencent path is unaffected: handleSubmit already acquires a fresh proof
per submit, and turnstile_enabled is false in that configuration.
Verified with the component spec (11 passed) and vue-tsc --noEmit (clean).
Review follow-ups on the reset-credit caching flow:
- Recover account state BEFORE (and independently of) the reset-credit
display cache. A failed cache refresh could previously abort the run and
leave the account rate-limited — the very reason the credit was spent
(#3672 / #3740). The recovered account row is now returned even when the
cache refresh fails.
- Run the post-reset bookkeeping on a detached, time-boxed context and give
the panel reset call a larger timeout. A client abort no longer strands a
consumed (non-refundable) credit with an unrecovered account, and the
chained upstream calls can no longer exceed the client timeout and invite a
retry that spends a second credit.
- Persist the reset-credit snapshot through POST /accounts/:id/quota/refresh
instead of a side-effecting GET flag, so the write is covered by the audit
middleware. A rejected snapshot write now degrades to cache_persisted=false
instead of turning a successful upstream read into a 502 that left the card
without a credit count and the reset button permanently disabled.
- Reject snapshots whose positive count carries no expiration entries, and
drop expired credits (clamping the count) when rehydrating, so a stale
cache can no longer light up the reset button.
- Keep nil quota / rate-limit services nil in the handler's interface fields;
storing a nil *Service made the "not enabled" guards non-nil.
- Time-box the usage-refresh suppression and reuse handleAccountUpdated so the
patched row also enters the auto-refresh silent window.
AdminRefundDialog already implements a `requireForce` prop that renders the
force checkbox and blocks submit until it is checked, but AdminOrdersView
never consumed `require_force` from the refund response nor bound the prop,
so the checkbox could never render. Admins hitting a require-force case (for
example a user who spent their balance after requesting the refund) only saw
an error toast and had no way to complete the refund from the UI.
Keep the dialog open when the backend answers `require_force`, surface the
checkbox and the backend warning, and reset the flag whenever the dialog is
opened or closed.
Verified with `npm run typecheck` (vue-tsc --noEmit, clean).