mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
feat(devices): accept a Live Activity push token on registration
Add an optional hex liveActivityPushToken to device registration. The iOS client registers the current per-Activity ActivityKit push token so the server can address a Live Activity update or end push on a medication-intake mutation, and sends null to clear it when no Activity is running. Following the medicationDelivery override convention, the field is only touched when present, so an ordinary re-register keeps the prior value. Regenerate the OpenAPI contract.
This commit is contained in:
1 parent
836fd7405e
commit
6d8dd75fff
4 files changed
+85
No files matched your search
@@ -5874,6 +5874,16 @@ components:
|
||||
- server
|
||||
- client
|
||||
- type: "null"
|
||||
liveActivityPushToken:
|
||||
description: v1.17.1 (#22) hex-encoded ActivityKit Live Activity push token (distinct from the device APNs token). The
|
||||
server addresses a Live Activity update / end push on a medication-intake mutation. Omitted = keep the prior
|
||||
value; null = clear it when the Activity ends.
|
||||
anyOf:
|
||||
- type: string
|
||||
minLength: 8
|
||||
maxLength: 256
|
||||
pattern: ^[A-Fa-f0-9]+$
|
||||
- type: "null"
|
||||
required:
|
||||
- token
|
||||
- bundleId
|
||||
|
||||
@@ -250,6 +250,52 @@ describe("POST /api/devices", () => {
|
||||
);
|
||||
expect(res.status).toBe(201);
|
||||
});
|
||||
|
||||
// v1.17.1 (#22) — Live Activity push-token registration.
|
||||
it("persists liveActivityPushToken on create when supplied", async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(SESSION_OK as never);
|
||||
const res = await POST(
|
||||
req({
|
||||
token: "abcd1234efgh5678",
|
||||
bundleId: "io.healthlog.app",
|
||||
liveActivityPushToken: "cafebabe".repeat(8),
|
||||
}),
|
||||
);
|
||||
expect(res.status).toBe(201);
|
||||
const data = vi.mocked(prisma.device.create).mock.calls[0][0].data;
|
||||
expect(data.liveActivityPushToken).toBe("cafebabe".repeat(8));
|
||||
});
|
||||
|
||||
it("clears liveActivityPushToken on re-register when null is sent", async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(SESSION_OK as never);
|
||||
vi.mocked(prisma.device.findUnique).mockResolvedValue({
|
||||
id: "dev-1",
|
||||
userId: "user-1",
|
||||
token: "abcd1234efgh5678",
|
||||
} as never);
|
||||
const res = await POST(
|
||||
req({
|
||||
token: "abcd1234efgh5678",
|
||||
bundleId: "io.healthlog.app",
|
||||
liveActivityPushToken: null,
|
||||
}),
|
||||
);
|
||||
expect(res.status).toBe(201);
|
||||
const data = vi.mocked(prisma.device.update).mock.calls[0][0].data;
|
||||
expect(data.liveActivityPushToken).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects a non-hex liveActivityPushToken with 422", async () => {
|
||||
vi.mocked(getSession).mockResolvedValue(SESSION_OK as never);
|
||||
const res = await POST(
|
||||
req({
|
||||
token: "abcd1234efgh5678",
|
||||
bundleId: "io.healthlog.app",
|
||||
liveActivityPushToken: "not-hex-!!!",
|
||||
}),
|
||||
);
|
||||
expect(res.status).toBe(422);
|
||||
});
|
||||
});
|
||||
|
||||
describe("POST /api/devices — 422 multi-issue envelope (v1.4.43 W6)", () => {
|
||||
|
||||
@@ -63,6 +63,19 @@ const deviceSchema = z
|
||||
// server APNs for this device; "client" forces local. Stored +
|
||||
// echoed; cron suppression stays user-level.
|
||||
medicationDelivery: z.enum(["server", "client"]).nullable().optional(),
|
||||
// v1.17.1 (#22) — ActivityKit issues a per-Activity push token distinct
|
||||
// from the device APNs token. The iOS client registers the current one
|
||||
// here so the server can address a Live Activity update / end push on a
|
||||
// medication-intake mutation; it sends `null` to clear the token when no
|
||||
// Activity is running. Hex, like the APNs token. Only touched when the
|
||||
// field is present so an ordinary re-register keeps the prior value.
|
||||
liveActivityPushToken: z
|
||||
.string()
|
||||
.min(8)
|
||||
.max(256)
|
||||
.regex(/^[A-Fa-f0-9]+$/, "liveActivityPushToken must be hex")
|
||||
.nullable()
|
||||
.optional(),
|
||||
})
|
||||
.refine(
|
||||
(v) =>
|
||||
@@ -146,6 +159,7 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
apnsToken,
|
||||
apnsEnvironment,
|
||||
medicationDelivery,
|
||||
liveActivityPushToken,
|
||||
} = parsed.data;
|
||||
|
||||
// APNs-token collision lookup. Two cases:
|
||||
@@ -212,6 +226,9 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
// v1.7.0 — only touch the override when the client sends the
|
||||
// field, so a re-register that omits it keeps the prior value.
|
||||
...(medicationDelivery !== undefined && { medicationDelivery }),
|
||||
// v1.17.1 (#22) — same omit-keeps-prior rule; `null` explicitly
|
||||
// clears the token when the Activity ends.
|
||||
...(liveActivityPushToken !== undefined && { liveActivityPushToken }),
|
||||
lastSeen: new Date(),
|
||||
},
|
||||
select: { id: true },
|
||||
@@ -231,6 +248,8 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
apnsEnvironment: apnsEnvironment ?? null,
|
||||
// v1.7.0 — per-device delivery override (null = inherit default).
|
||||
...(medicationDelivery !== undefined && { medicationDelivery }),
|
||||
// v1.17.1 (#22) — Live Activity push token (null = no Activity).
|
||||
...(liveActivityPushToken !== undefined && { liveActivityPushToken }),
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
@@ -43,6 +43,16 @@ const deviceRegisterRequest = z
|
||||
.describe(
|
||||
'v1.7.0 per-device medication-delivery override. NULL / omitted = inherit the user-level roaming default. "server" forces server APNs for this device; "client" forces local. Stored + echoed; cron suppression stays user-level.',
|
||||
),
|
||||
liveActivityPushToken: z
|
||||
.string()
|
||||
.min(8)
|
||||
.max(256)
|
||||
.regex(/^[A-Fa-f0-9]+$/)
|
||||
.nullable()
|
||||
.optional()
|
||||
.describe(
|
||||
"v1.17.1 (#22) hex-encoded ActivityKit Live Activity push token (distinct from the device APNs token). The server addresses a Live Activity update / end push on a medication-intake mutation. Omitted = keep the prior value; null = clear it when the Activity ends.",
|
||||
),
|
||||
})
|
||||
.meta({
|
||||
id: "DeviceRegisterRequest",
|
||||
|
||||
Reference in new issue
Block a user