21-Jun-2026 — build the push service worker in the image, harden Google Cloud setup, and make push de... #293
build 713 — commit 63fc039
Follow-ups to the Gmail inbox work, found testing on staging and EKWG:
Dockerfile: the image build ran
npx ng builddirectly, which skips theprebuildnpm hook that compiles inbox-push-sw.ts into dist-sw/. The Angular asset copy then had nothing to copy, so /inbox-push-sw.js returned 404 in every deployed image and the inbox web-push service worker failed to register (it only worked locally, wherenpm run buildtriggersprebuild). The image build now runsnpm run build:swbeforeng build.inbox-google-setup: setup is now resilient on a project where the required APIs are not already enabled. It enables the Cloud Resource Manager, Gmail and Pub/Sub APIs and resolves the project's canonical ID via Resource Manager. Google's API enablement takes a few minutes to propagate, so the project lookup and the Pub/Sub steps (create topic, grant publisher, create push subscription) now retry with backoff over roughly 2.5 minutes when they hit the "API has not been used before or is disabled" error, instead of failing. Crucially, the topic and subscription are now named with the project ID rather than the project number: Gmail's users.watch validates the topic name against the project ID, so the number-form name was rejected ("Invalid topicName does not match projects/
/topics/*") even though Pub/Sub itself accepts the number. This also corrects the stored pubsubProjectId from the display name to the real ID. system-inbox-mailbox-connections: switching a mailbox to push now always re-fetches the configured Pub/Sub topic before applying the watch, rather than trusting a cached value that can be stale right after setup (which completes in the background). So push applies with the correct topic without a manual page refresh.
inbox-notification.service: the header unread badge now wires its websocket subscriptions before connecting (and relies on the client's own reconnect), so a failed or dropped initial connection - e.g. while a deploy restarts the app - no longer leaves the badge without a live feed. It picks up the next event and updates without a page refresh.