Repository navigation
Commit e8dc483
feat(messaging): store a push device's origin at registration and carry it in every push to the device (#30705)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DUpvxmC7UGJmuMQW4ZsJoLR)_
domain:server#3 · os-elon-musk · session_016WV4czbHcjvoBYWDw2gRAq
Fixes #29768
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:手机注册推送设备时带上的 `origin`(控制台加载自哪台服务器)现在会存到 `sys_user_device`
上,`listDevices` 读得回来,每条推送的 `data` 里都带着它。
- 为什么改:裁决 B(#29642)的服务端一半。保存了两台服务器的手机壳,点开 B 服务器发来的推送会落到 A 上;有了
`origin`,壳就能按它打开正确的服务器。
- 风险与回滚:新增一个可空列,不带 `origin` 的注册与推送完全照旧;spec 只改描述和注释,不改校验。回滚即 revert。
- 先看:`packages/services/service-messaging/src/device-registry.ts` 的写行和
`toUserDevice`;推送通道的复制在 main 上已存在(`push-channel.ts` 的 `withOrigin`),本 PR
未改它。
- 两分钟验证:`pnpm --filter @objectstack/service-messaging exec vitest run
src/device-registry.integration.test.ts`,预期 14 passed。
- 需要你做的:无;席位验收、CI 全绿后落地。
## What changes
**`@objectstack/service-messaging`.**
- `sys_user_device` gains an optional `origin` text column, deliberately
without `maxLength`: spec's origin pattern bounds the shape, not the
length, so a bounded column could refuse a value the registration
accepts.
- `DeviceRegistry` writes `origin: request.origin ?? null` beside
`app_id` / `locale` on both the insert and the refresh (a refresh
replaces it, as it does those two), and `toUserDevice` sets
`device.origin` when the row holds one.
- The push channel already copied `device.origin` into each device's
push `data` (`withOrigin` in `push-channel.ts`, landed with the
channel); until now the registry never stored one, so nothing reached
it. No change there.
- The field's label and help are translated in all ten shipped
translated locales, ru, de and ko included since main added them
(`check-i18n-bundles.mjs --write`, then hand translation, then `--write`
again; the en bundle is regenerated from the source).
**`@objectstack/spec`, description and docblock only.**
- `RegisterDeviceRequestSchema.origin` and `UserDeviceSchema.origin`
drop their `[PLANNED — not yet stored]` prefix; the marker pin in
`push-server-origin.test.ts` is flipped to assert that no origin member
is marked PLANNED.
- `push-server-origin.ts` docblock takes the three should-fix items of
contract review 6595162668 by the docblock route: it names the DNS-label
limit (`_` and a trailing-dot host are refused although
`window.location.origin` can answer them), the port and IPv6 laxness
with the Console as the one producer, and that a dotted IPv4 host is
accepted. ⛔ The validator is unchanged.
- The `RegisterDeviceRequestSchema` docblock's refresh list now names
every request member a refresh rewrites: token, vendor, platform, app,
locale, origin and name.
- Regenerated: `content/docs/references/api/protocol.mdx`,
`content/docs/references/system/push-device.mdx` (`gen:schema &&
gen:docs`).
**`@objectstack/service-push`, docblock only.** The `PushMessage`
docblock in `push-service.ts` names `origin` as the third delivery-data
member, present when the device registered one; the changeset carries
`'@objectstack/service-push': patch` for it.
**Not changed, verified:**
`packages/runtime/src/domains/notifications.ts` already hands
`RegisterDeviceRequestSchema.safeParse(body).data` to `registerDevice`,
so `origin` reaches the registry. Both transports in `service-push`
spread `message.data` unchanged (FCM into `message.data`, APNs at the
payload top level).
## Why
Ruling B on #29642 (record 6593872191, maintainer 「同意」): "the Console
sends the origin it was loaded from in `RegisterDeviceRequest`. The
server stores it on the `UserDevice` row, and the push channel copies it
into `PushDeliveryDataSchema`." The spec half (PR #29767) is merged;
this is the server half. The Console producer stays with #29042.
## Verification
Measured on `origin/main` `b77c160a` (registry and object reverted to
main, the new pins kept): a registration with `origin:
'https://crm.example.com'` stored a row whose `origin` is `undefined`,
`listDevices` answered no `origin`, and a push through the real channel
over the real registry carried `{ notificationId, route }` only.
- `src/device-registry.integration.test.ts`, new top-level describe (4,
real `ObjectQL` over `SqlDriver`): the registration's origin is stored
and `listDevices` answers it; a registration without one stores `null`
and the device has no `origin` member; a refresh replaces the stored
origin and a refresh without one clears it; a push through
`createPushChannel` with the `MessagingService` as its device registry
carries the stored origin for that device and none for a device without
one, and every data object parses as `PushDeliveryDataSchema`.
- `src/objects/user-device.object.test.ts` (+1): the `origin` column is
optional text with no `maxLength`.
- `packages/spec/src/system/push-server-origin.test.ts`: the marker pin
flipped.
- Ablations: registry and object at main ⇒ 4 red (the null/absent case
is the negative control and stays green); only the read-back line
removed ⇒ 2 red (list and push), the row pin green; the PLANNED prefix
put back on `UserDeviceSchema.origin` ⇒ the flipped pin red. All green
at head.
- `@objectstack/service-messaging`: 66 files / 850 tests green at
`48036dd1`, 67 files / 858 tests green on the merged tree at `6dc84f5e`;
`typecheck` green. `check-i18n-bundles.mjs`: in sync;
`check:i18n-coverage`: none new. `@objectstack/spec` `check:generated`:
every artifact current (`check:test-typecheck` needs an 8 GB heap in
this container; green with it).
- Every `dispatch-gates --commands` family at `48036dd1`: 108 derived,
108 run, all exit 0, 0 NOT-MEASURED (`--ran` reconcile). `pnpm lint`:
exit 0. The same sweep on the final head `6dc84f5e` is reported on the
card.
Skills-docs: `git grep -n -i -E
"registerDevice|notifications/devices|RegisterDeviceRequest|PushDeliveryData|push.*origin"
-- skills content/docs` read: no `skills/**` hit;
`content/docs/kernel/runtime-services/push-service.mdx` already lists
`origin` among the registration members and says nothing about it being
unstored, still true; release notes untouched. Generated
`content/docs/references/**` regenerated, not hand-edited.
Contract review 6672159811 (PASS on `48036dd1`) left two should-fix
docblock items, the `PushMessage` data list and the refresh list; both
are folded in at `7e4b969d`, delta review 6672299353 PASS. `ccea409a`
merges `main`, whose new ru, de and ko service-messaging bundles lacked
the new key (CI `check:i18n` / `check:i18n-coverage` on the merge ref),
and `6dc84f5e` translates it in those three.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 block is the
lane's maintainer summary).
<!-- signed: domain:server#3 · os-elon-musk ·
session_016WV4czbHcjvoBYWDw2gRAq -->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 96a7d20 commit e8dc483
23 files changed
Lines changed: 190 additions & 22 deletions
File tree
- .changeset
- content/docs/references
- api
- system
- packages
- services
- service-messaging/src
- objects
- translations
- service-push/src
- spec/src
- api
- system
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2782 | 2782 | | |
2783 | 2783 | | |
2784 | 2784 | | |
2785 | | - | |
| 2785 | + | |
2786 | 2786 | | |
2787 | 2787 | | |
2788 | 2788 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
41 | | - | |
| 41 | + | |
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
28 | | - | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
29 | 32 | | |
30 | 33 | | |
31 | 34 | | |
| |||
35 | 38 | | |
36 | 39 | | |
37 | 40 | | |
| 41 | + | |
38 | 42 | | |
39 | 43 | | |
40 | 44 | | |
| 45 | + | |
41 | 46 | | |
42 | 47 | | |
43 | 48 | | |
| |||
260 | 265 | | |
261 | 266 | | |
262 | 267 | | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
186 | 186 | | |
187 | 187 | | |
188 | 188 | | |
| 189 | + | |
189 | 190 | | |
190 | 191 | | |
191 | 192 | | |
| |||
296 | 297 | | |
297 | 298 | | |
298 | 299 | | |
| 300 | + | |
299 | 301 | | |
300 | 302 | | |
301 | 303 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
33 | 43 | | |
34 | 44 | | |
35 | 45 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
124 | 135 | | |
125 | 136 | | |
126 | 137 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
462 | 462 | | |
463 | 463 | | |
464 | 464 | | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
465 | 469 | | |
466 | 470 | | |
467 | 471 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
462 | 462 | | |
463 | 463 | | |
464 | 464 | | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
465 | 469 | | |
466 | 470 | | |
467 | 471 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
462 | 462 | | |
463 | 463 | | |
464 | 464 | | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
465 | 469 | | |
466 | 470 | | |
467 | 471 | | |
| |||
0 commit comments