fix(export): per-platform polish from the on-device review
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 1m4s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m41s

- TG iOS: the OS share sheet with the fetched file (WKWebView has
  navigator.share — the долгоживущий debug-overlay Share path proves
  it). The chooser stays the app modal on this one platform: a native
  popup callback cannot supply the user activation the sheet needs.
  TG Android/desktop keep the native popup + downloadFile (verified
  perfect, untouched).
- VK mobile: the PNG now rides VKWebAppDownloadFile like the GCG — the
  owner-verified path that lands in VK's native share flow; the
  ShowImages viewer is dropped (its preview offered save only, no
  share).
- VK desktop iframe: both formats are plain anchor downloads (it is an
  ordinary browser; the viewer/bridge path made no sense there).
This commit is contained in:
Ilia Denisov
2026-07-02 23:05:35 +02:00
parent a9376c20a2
commit 8db18f2a2a
8 changed files with 142 additions and 83 deletions
+9 -8
View File
@@ -794,14 +794,15 @@ platform: the client calls the authenticated `game.export_url` op, the backend m
**relative, HMAC-signed, short-lived path**
(`/dl/{game}/{kind}?e=<expiry>&…&s=<HMAC-SHA256>`, 10-minute TTL,
`BACKEND_EXPORT_SIGN_KEY`), and the client resolves it against its **own origin** (no
service ever needs to know the public host) and hands it to the platform's most
native affordance: Telegram `downloadFile` (Bot API 8.0; the chooser there is the
native showPopup — the whole TG chain is activation-free bridge calls), VK's native
photo viewer for the PNG (`VKWebAppShowImages`) + `VKWebAppDownloadFile` for the
GCG, the OS share sheet with the fetched file on a mobile browser, or a plain
anchor download (desktop, and the VK desktop iframe fallback). The gateway serves
the bytes via `http.ServeContent` (Content-Length + Range/206 — Android's system
DownloadManager hangs on chunked bodies of unknown length). The GET is the gateway's
service ever needs to know the public host) and hands it to the best affordance
each platform has: Telegram Android/desktop `downloadFile` (Bot API 8.0; the
chooser there is the native showPopup — activation-free bridge calls end to end),
Telegram iOS the OS share sheet with the fetched file (the app-modal click supplies
the user activation a popup callback lacks), VK mobile `VKWebAppDownloadFile` for
both formats, the OS share sheet on a mobile browser, or a plain anchor download
(desktop browsers and the VK desktop iframe). The gateway serves the bytes via
`http.ServeContent` (Content-Length + Range/206 — Android's system DownloadManager
hangs on chunked bodies of unknown length). The GET is the gateway's
**only unauthenticated data route** (`/dl/*` — in the caddy `@gateway` matcher and the
per-IP public rate limiter): the native download calls carry no cookies or headers, so
the URL's signature — verified by the backend on its `/api/v1/public` group in constant