Files
gitmost/docs/mobile-bootstrap.md
a 41e91c26e4 fix(offline): address PR #120 review (cross-user leak, Yjs title dup, i18n, docs, guards)
Security:
- Clear the offline IndexedDB cache on sign-in (not only logout) so a previous
  user's persisted query cache and Yjs page bodies cannot leak to the next user
  on a shared device when the prior session ended without an explicit logout.

Regressions:
- Remove the double Yjs title write from the AI title-generation path: the title
  editor is bound to the Yjs `title` fragment and the server REST update reseeds
  it, so the local setContent raced that reseed and doubled/garbled the title.

Conventions / i18n / docs:
- Remove the unused showAiMenuAtom.
- Register the 3 offline-fallback strings in en-US and ru-RU.
- Fix the 5 broken links to the nonexistent docs/offline-sync-plan.md.

Stability / simplification:
- warmInfiniteAll now reports truncation (returns false) when it hits maxPages
  with a cursor still pending instead of silently succeeding.
- space-tree make-offline catch logs the raw error and surfaces the real cause.
- Move the Offline/Mobile/CORS CHANGELOG entries from the released 0.93.0 section
  into [Unreleased] (CORS is a documented breaking change).
- Drop the pass-through sync-flag forwarders in use-page-collab-providers; set the
  atoms directly.
- Collapse the three isSwaggerEnabled true-cases into it.each.

Tests / architecture:
- Extract collabTokenNeedsRefresh (pure) and cover all four token states.
- Extract shouldPropagateTitleChange and cover the collab-origin skip; add a
  TitleEditor render test for the static-h1 vs collaborative-editor switch.
- Add a use-auth test asserting the sign-in cache purge runs before login.
- Add an OFFLINE_PERSIST_ROOTS guard test asserting every persisted root maps to
  an exported query-key factory; route make-offline's currentUser warm through a
  new userKeys factory.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 22:29:03 +03:00

62 lines
3.8 KiB
Markdown

# Mobile app bootstrap
Purpose: this document records what has been bootstrapped in the repo to enable a
mobile app for Gitmost, per the first-step checklist in
[docs/mobile-app-plan.md](./mobile-app-plan.md) section 12.
## What is in the repo now
- **PWA**: web app manifest plus a service worker generated by `vite-plugin-pwa`
using Workbox (`strategies: "generateSW"` — not hand-rolled). The SW is built
for production only (`devOptions: { enabled: false }`) and uses
`registerType: "prompt"`, so the user is asked to apply an update rather than it
auto-updating; registration goes through `virtual:pwa-register/react`
(`useRegisterSW`) in `apps/client/src/pwa/pwa-update-prompt.tsx`, mounted from
`main.tsx` and skipped inside the Capacitor native WebView. The SW precaches the
app shell (`globPatterns` js/css/html/...) and serves `navigateFallback:
"index.html"` for SPA routes, with `navigateFallbackDenylist` excluding the
server-owned routes `/api`, `/collab`, `/socket.io`, `/share/`, `/mcp`, and
`/robots.txt`. `runtimeCaching` keeps `/collab`, `/socket.io`, and all `/api`
as `NetworkOnly` — offline reads are served by the persisted TanStack Query
cache (IndexedDB) and `y-indexeddb` for the page Yjs doc, not by an SW HTTP
cache. This lets the existing responsive web UI be installed and run as a
Progressive Web App. The offline/sync design (stages M0…M4) is summarized in
[mobile-app-plan.md](./mobile-app-plan.md).
- **Backend mobile auth**: opt-in token return from the login flow. The login
request accepts a `returnToken` flag (must be sent as a JSON boolean) that makes
the server include the auth token in the response body, and the server already
accepts a `Bearer` token in the `Authorization` header. Note the global response
interceptor wraps every payload, so the native client reads the token at
`response.data.authToken` (not at the top level). A native client can store this
token (Keychain / Keystore) and send it as `Authorization: Bearer` on each request.
- **Explicit CORS allowlist**: the server reads a `CORS_ALLOWED_ORIGINS` env
variable for the allowed origins, and always allows the native WebView origins
(`capacitor://localhost`, `ionic://localhost`, `https://localhost`) so the
mobile shell can call the API.
- **Optional OpenAPI / Swagger**: an opt-in OpenAPI/Swagger surface gated behind
the `SWAGGER_ENABLED` env flag, useful for developing the native client.
- **Capacitor config**: [capacitor.config.ts](../capacitor.config.ts) at the
repo root. It targets the `apps/client` web build output (`apps/client/dist`)
for the Android bundled mode, and on iOS loads the client from a hosted server
via `CAP_SERVER_URL` (`server.url`) so the AGPL web client is not bundled into
the `.ipa` (see mobile-app-plan section 9).
## Remaining MANUAL / local steps (require Xcode / external accounts, out of scope here)
- Run `pnpm install` to fetch the Capacitor packages and `@nestjs/swagger`.
- Run `pnpm run client:build` to produce the web build in `apps/client/dist`.
- Run `npx cap add ios` and/or `npx cap add android` to generate the native
platform projects (these live outside version control; see `.gitignore`).
- Set `CAP_SERVER_URL` for the iOS build so the shell loads the hosted client
(AGPL-clean), then run `pnpm run mobile:build` / `cap sync`.
- Set up push notifications: APNs for iOS and FCM for Android.
- Obtain an Apple Developer account and the App Store / Play Console listings.
- Confirm the AGPL iOS distribution decision (mobile-app-plan section 9) before
shipping anything to the App Store.
## See also
For the full background, rationale, and the licensing analysis, see
[docs/mobile-app-plan.md](./mobile-app-plan.md) (section 12 for the bootstrap
checklist, section 9 for the AGPL / App Store licensing path).