mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-16 12:30:49 +00:00
feat(push): use scoped relay send grants
This commit is contained in:
@@ -105,7 +105,7 @@ pnpm ios:beta -- --build-number 7
|
||||
## APNs Expectations For Official Builds
|
||||
|
||||
- Official/TestFlight builds register with the external push relay before they publish `push.apns.register` to the gateway.
|
||||
- The gateway registration for relay mode contains an opaque relay handle and installation metadata instead of the raw APNs token.
|
||||
- The gateway registration for relay mode contains an opaque relay handle, a registration-scoped send grant, and installation metadata instead of the raw APNs token.
|
||||
- The app persists the relay handle metadata locally so reconnects can republish the gateway registration without re-registering on every connect.
|
||||
- Relay mode requires a reachable relay base URL and uses App Attest plus the app receipt during registration.
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ private struct DirectGatewayPushRegistrationPayload: Encodable {
|
||||
private struct RelayGatewayPushRegistrationPayload: Encodable {
|
||||
var transport: String = PushTransportMode.relay.rawValue
|
||||
var relayHandle: String
|
||||
var sendGrant: String
|
||||
var installationId: String
|
||||
var topic: String
|
||||
var environment: String
|
||||
@@ -76,6 +77,7 @@ actor PushRegistrationManager {
|
||||
return try Self.encodePayload(
|
||||
RelayGatewayPushRegistrationPayload(
|
||||
relayHandle: stored.relayHandle,
|
||||
sendGrant: stored.sendGrant,
|
||||
installationId: installationId,
|
||||
topic: topic,
|
||||
environment: self.buildConfig.apnsEnvironment.rawValue,
|
||||
@@ -92,6 +94,7 @@ actor PushRegistrationManager {
|
||||
apnsTokenHex: apnsTokenHex)
|
||||
let registrationState = PushRelayRegistrationStore.RegistrationState(
|
||||
relayHandle: response.relayHandle,
|
||||
sendGrant: response.sendGrant,
|
||||
relayHandleExpiresAtMs: response.expiresAtMs,
|
||||
tokenDebugSuffix: Self.normalizeTokenSuffix(response.tokenSuffix),
|
||||
lastAPNsTokenHashHex: tokenHashHex,
|
||||
@@ -101,6 +104,7 @@ actor PushRegistrationManager {
|
||||
return try Self.encodePayload(
|
||||
RelayGatewayPushRegistrationPayload(
|
||||
relayHandle: response.relayHandle,
|
||||
sendGrant: response.sendGrant,
|
||||
installationId: installationId,
|
||||
topic: topic,
|
||||
environment: self.buildConfig.apnsEnvironment.rawValue,
|
||||
|
||||
@@ -71,6 +71,7 @@ private struct PushRelayRegisterRequest: Encodable {
|
||||
|
||||
struct PushRelayRegisterResponse: Decodable {
|
||||
var relayHandle: String
|
||||
var sendGrant: String
|
||||
var expiresAtMs: Int64?
|
||||
var tokenSuffix: String?
|
||||
var status: String
|
||||
|
||||
@@ -2,6 +2,7 @@ import Foundation
|
||||
|
||||
private struct StoredPushRelayRegistrationState: Codable {
|
||||
var relayHandle: String
|
||||
var sendGrant: String
|
||||
var relayHandleExpiresAtMs: Int64?
|
||||
var tokenDebugSuffix: String?
|
||||
var lastAPNsTokenHashHex: String
|
||||
@@ -17,6 +18,7 @@ enum PushRelayRegistrationStore {
|
||||
|
||||
struct RegistrationState: Codable {
|
||||
var relayHandle: String
|
||||
var sendGrant: String
|
||||
var relayHandleExpiresAtMs: Int64?
|
||||
var tokenDebugSuffix: String?
|
||||
var lastAPNsTokenHashHex: String
|
||||
@@ -35,6 +37,7 @@ enum PushRelayRegistrationStore {
|
||||
}
|
||||
return RegistrationState(
|
||||
relayHandle: decoded.relayHandle,
|
||||
sendGrant: decoded.sendGrant,
|
||||
relayHandleExpiresAtMs: decoded.relayHandleExpiresAtMs,
|
||||
tokenDebugSuffix: decoded.tokenDebugSuffix,
|
||||
lastAPNsTokenHashHex: decoded.lastAPNsTokenHashHex,
|
||||
@@ -46,6 +49,7 @@ enum PushRelayRegistrationStore {
|
||||
static func saveRegistrationState(_ state: RegistrationState) -> Bool {
|
||||
let stored = StoredPushRelayRegistrationState(
|
||||
relayHandle: state.relayHandle,
|
||||
sendGrant: state.sendGrant,
|
||||
relayHandleExpiresAtMs: state.relayHandleExpiresAtMs,
|
||||
tokenDebugSuffix: state.tokenDebugSuffix,
|
||||
lastAPNsTokenHashHex: state.lastAPNsTokenHashHex,
|
||||
|
||||
@@ -329,6 +329,7 @@ describe("node.invoke APNs wake path", () => {
|
||||
nodeId: "ios-node-relay",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
@@ -340,7 +341,6 @@ describe("node.invoke APNs wake path", () => {
|
||||
ok: true,
|
||||
value: {
|
||||
baseUrl: "https://relay.example.com",
|
||||
authToken: "relay-secret",
|
||||
timeoutMs: 1000,
|
||||
},
|
||||
});
|
||||
@@ -373,6 +373,7 @@ describe("node.invoke APNs wake path", () => {
|
||||
nodeId: "ios-node-relay",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
|
||||
@@ -119,6 +119,7 @@ describe("push.test handler", () => {
|
||||
nodeId: "ios-node-1",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-1",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
@@ -130,7 +131,6 @@ describe("push.test handler", () => {
|
||||
ok: true,
|
||||
value: {
|
||||
baseUrl: "https://relay.example.com",
|
||||
authToken: "relay-secret",
|
||||
timeoutMs: 1000,
|
||||
},
|
||||
});
|
||||
@@ -213,6 +213,7 @@ describe("push.test handler", () => {
|
||||
nodeId: "ios-node-1",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
@@ -224,7 +225,6 @@ describe("push.test handler", () => {
|
||||
ok: true,
|
||||
value: {
|
||||
baseUrl: "https://relay.example.com",
|
||||
authToken: "relay-secret",
|
||||
timeoutMs: 1000,
|
||||
},
|
||||
});
|
||||
@@ -252,6 +252,7 @@ describe("push.test handler", () => {
|
||||
nodeId: "ios-node-1",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
|
||||
@@ -290,6 +290,7 @@ describe("node exec events", () => {
|
||||
payloadJSON: JSON.stringify({
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
@@ -302,6 +303,7 @@ describe("node exec events", () => {
|
||||
nodeId: "node-relay",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
|
||||
@@ -598,6 +598,7 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt
|
||||
nodeId,
|
||||
transport: "relay",
|
||||
relayHandle: typeof obj.relayHandle === "string" ? obj.relayHandle : "",
|
||||
sendGrant: typeof obj.sendGrant === "string" ? obj.sendGrant : "",
|
||||
installationId: typeof obj.installationId === "string" ? obj.installationId : "",
|
||||
topic,
|
||||
environment,
|
||||
|
||||
@@ -3,12 +3,18 @@ import type { AddressInfo } from "node:net";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const TEST_GATEWAY_TOKEN = "test-gateway-token-1234567890";
|
||||
type BeforeToolCallHookResult =
|
||||
| { blocked: true; reason: string }
|
||||
| { blocked: false; params: unknown };
|
||||
|
||||
const hookMocks = vi.hoisted(() => ({
|
||||
resolveToolLoopDetectionConfig: vi.fn(() => ({ warnAt: 3 })),
|
||||
runBeforeToolCallHook: vi.fn(async ({ params }: { params: unknown }) => ({
|
||||
blocked: false as const,
|
||||
params,
|
||||
})),
|
||||
runBeforeToolCallHook: vi.fn(
|
||||
async ({ params }: { params: unknown }): Promise<BeforeToolCallHookResult> => ({
|
||||
blocked: false,
|
||||
params,
|
||||
}),
|
||||
),
|
||||
}));
|
||||
|
||||
let cfg: Record<string, unknown> = {};
|
||||
|
||||
@@ -4,7 +4,6 @@ export type ApnsRelayPushType = "alert" | "background";
|
||||
|
||||
export type ApnsRelayConfig = {
|
||||
baseUrl: string;
|
||||
authToken: string;
|
||||
timeoutMs: number;
|
||||
};
|
||||
|
||||
@@ -23,6 +22,7 @@ export type ApnsRelayPushResponse = {
|
||||
|
||||
export type ApnsRelayRequestSender = (params: {
|
||||
relayConfig: ApnsRelayConfig;
|
||||
sendGrant: string;
|
||||
relayHandle: string;
|
||||
pushType: ApnsRelayPushType;
|
||||
priority: "10" | "5";
|
||||
@@ -71,12 +71,10 @@ export function resolveApnsRelayConfigFromEnv(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): ApnsRelayConfigResolution {
|
||||
const baseUrl = normalizeNonEmptyString(env.OPENCLAW_APNS_RELAY_BASE_URL);
|
||||
const authToken = normalizeNonEmptyString(env.OPENCLAW_APNS_RELAY_AUTH_TOKEN);
|
||||
if (!baseUrl || !authToken) {
|
||||
if (!baseUrl) {
|
||||
return {
|
||||
ok: false,
|
||||
error:
|
||||
"APNs relay config missing: set OPENCLAW_APNS_RELAY_BASE_URL and OPENCLAW_APNS_RELAY_AUTH_TOKEN",
|
||||
error: "APNs relay config missing: set OPENCLAW_APNS_RELAY_BASE_URL",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -106,7 +104,6 @@ export function resolveApnsRelayConfigFromEnv(
|
||||
ok: true,
|
||||
value: {
|
||||
baseUrl: parsed.toString().replace(/\/+$/, ""),
|
||||
authToken,
|
||||
timeoutMs: normalizeTimeoutMs(env.OPENCLAW_APNS_RELAY_TIMEOUT_MS),
|
||||
},
|
||||
};
|
||||
@@ -121,6 +118,7 @@ export function resolveApnsRelayConfigFromEnv(
|
||||
|
||||
async function sendApnsRelayRequest(params: {
|
||||
relayConfig: ApnsRelayConfig;
|
||||
sendGrant: string;
|
||||
relayHandle: string;
|
||||
pushType: ApnsRelayPushType;
|
||||
priority: "10" | "5";
|
||||
@@ -130,7 +128,7 @@ async function sendApnsRelayRequest(params: {
|
||||
method: "POST",
|
||||
redirect: "manual",
|
||||
headers: {
|
||||
authorization: `Bearer ${params.relayConfig.authToken}`,
|
||||
authorization: `Bearer ${params.sendGrant}`,
|
||||
"content-type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
@@ -177,6 +175,7 @@ async function sendApnsRelayRequest(params: {
|
||||
|
||||
export async function sendApnsRelayPush(params: {
|
||||
relayConfig: ApnsRelayConfig;
|
||||
sendGrant: string;
|
||||
relayHandle: string;
|
||||
pushType: ApnsRelayPushType;
|
||||
priority: "10" | "5";
|
||||
@@ -186,6 +185,7 @@ export async function sendApnsRelayPush(params: {
|
||||
const sender = params.requestSender ?? sendApnsRelayRequest;
|
||||
return await sender({
|
||||
relayConfig: params.relayConfig,
|
||||
sendGrant: params.sendGrant,
|
||||
relayHandle: params.relayHandle,
|
||||
pushType: params.pushType,
|
||||
priority: params.priority,
|
||||
|
||||
@@ -69,6 +69,7 @@ describe("push APNs registration store", () => {
|
||||
nodeId: "ios-node-relay",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
@@ -83,6 +84,7 @@ describe("push APNs registration store", () => {
|
||||
nodeId: "ios-node-relay",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
@@ -111,6 +113,7 @@ describe("push APNs registration store", () => {
|
||||
nodeId: "ios-node-relay",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "staging",
|
||||
@@ -123,6 +126,7 @@ describe("push APNs registration store", () => {
|
||||
nodeId: "ios-node-relay",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
@@ -140,6 +144,7 @@ describe("push APNs registration store", () => {
|
||||
nodeId: "ios-node-relay",
|
||||
transport: "relay",
|
||||
relayHandle: oversized,
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
@@ -152,6 +157,7 @@ describe("push APNs registration store", () => {
|
||||
nodeId: "ios-node-relay",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: oversized,
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
@@ -246,14 +252,12 @@ describe("push APNs env config", () => {
|
||||
it("resolves APNs relay config from env", () => {
|
||||
const resolved = resolveApnsRelayConfigFromEnv({
|
||||
OPENCLAW_APNS_RELAY_BASE_URL: "https://relay.example.com",
|
||||
OPENCLAW_APNS_RELAY_AUTH_TOKEN: "relay-secret",
|
||||
OPENCLAW_APNS_RELAY_TIMEOUT_MS: "2500",
|
||||
} as NodeJS.ProcessEnv);
|
||||
expect(resolved).toMatchObject({
|
||||
ok: true,
|
||||
value: {
|
||||
baseUrl: "https://relay.example.com",
|
||||
authToken: "relay-secret",
|
||||
timeoutMs: 2500,
|
||||
},
|
||||
});
|
||||
@@ -262,7 +266,6 @@ describe("push APNs env config", () => {
|
||||
it("rejects insecure APNs relay http URLs by default", () => {
|
||||
const resolved = resolveApnsRelayConfigFromEnv({
|
||||
OPENCLAW_APNS_RELAY_BASE_URL: "http://relay.example.com",
|
||||
OPENCLAW_APNS_RELAY_AUTH_TOKEN: "relay-secret",
|
||||
} as NodeJS.ProcessEnv);
|
||||
expect(resolved).toMatchObject({
|
||||
ok: false,
|
||||
@@ -276,14 +279,12 @@ describe("push APNs env config", () => {
|
||||
it("allows APNs relay http URLs only when explicitly enabled", () => {
|
||||
const resolved = resolveApnsRelayConfigFromEnv({
|
||||
OPENCLAW_APNS_RELAY_BASE_URL: "http://127.0.0.1:8787",
|
||||
OPENCLAW_APNS_RELAY_AUTH_TOKEN: "relay-secret",
|
||||
OPENCLAW_APNS_RELAY_ALLOW_HTTP: "true",
|
||||
} as NodeJS.ProcessEnv);
|
||||
expect(resolved).toMatchObject({
|
||||
ok: true,
|
||||
value: {
|
||||
baseUrl: "http://127.0.0.1:8787",
|
||||
authToken: "relay-secret",
|
||||
timeoutMs: 10_000,
|
||||
},
|
||||
});
|
||||
@@ -292,7 +293,6 @@ describe("push APNs env config", () => {
|
||||
it("rejects http relay URLs for non-loopback hosts even when explicitly enabled", () => {
|
||||
const resolved = resolveApnsRelayConfigFromEnv({
|
||||
OPENCLAW_APNS_RELAY_BASE_URL: "http://relay.example.com",
|
||||
OPENCLAW_APNS_RELAY_AUTH_TOKEN: "relay-secret",
|
||||
OPENCLAW_APNS_RELAY_ALLOW_HTTP: "true",
|
||||
} as NodeJS.ProcessEnv);
|
||||
expect(resolved).toMatchObject({
|
||||
@@ -307,7 +307,6 @@ describe("push APNs env config", () => {
|
||||
it("rejects APNs relay URLs with query, fragment, or userinfo components", () => {
|
||||
const withQuery = resolveApnsRelayConfigFromEnv({
|
||||
OPENCLAW_APNS_RELAY_BASE_URL: "https://relay.example.com/path?debug=1",
|
||||
OPENCLAW_APNS_RELAY_AUTH_TOKEN: "relay-secret",
|
||||
} as NodeJS.ProcessEnv);
|
||||
expect(withQuery.ok).toBe(false);
|
||||
if (!withQuery.ok) {
|
||||
@@ -316,7 +315,6 @@ describe("push APNs env config", () => {
|
||||
|
||||
const withUserinfo = resolveApnsRelayConfigFromEnv({
|
||||
OPENCLAW_APNS_RELAY_BASE_URL: "https://user:pass@relay.example.com/path",
|
||||
OPENCLAW_APNS_RELAY_AUTH_TOKEN: "relay-secret",
|
||||
} as NodeJS.ProcessEnv);
|
||||
expect(withUserinfo.ok).toBe(false);
|
||||
if (!withUserinfo.ok) {
|
||||
@@ -468,13 +466,13 @@ describe("push APNs send semantics", () => {
|
||||
const result = await sendApnsAlert({
|
||||
relayConfig: {
|
||||
baseUrl: "https://relay.example.com",
|
||||
authToken: "relay-secret",
|
||||
timeoutMs: 1000,
|
||||
},
|
||||
registration: {
|
||||
nodeId: "ios-node-relay",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
@@ -520,9 +518,9 @@ describe("push APNs send semantics", () => {
|
||||
const result = await sendApnsRelayPush({
|
||||
relayConfig: {
|
||||
baseUrl: "https://relay.example.com",
|
||||
authToken: "relay-secret",
|
||||
timeoutMs: 1000,
|
||||
},
|
||||
sendGrant: "send-grant-123",
|
||||
relayHandle: "relay-handle-123",
|
||||
payload: { aps: { "content-available": 1 } },
|
||||
pushType: "background",
|
||||
@@ -568,6 +566,7 @@ describe("push APNs send semantics", () => {
|
||||
nodeId: "ios-node-relay",
|
||||
transport: "relay",
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
|
||||
@@ -29,6 +29,7 @@ export type RelayApnsRegistration = {
|
||||
nodeId: string;
|
||||
transport: "relay";
|
||||
relayHandle: string;
|
||||
sendGrant: string;
|
||||
installationId: string;
|
||||
topic: string;
|
||||
environment: "production";
|
||||
@@ -97,6 +98,7 @@ type RegisterRelayApnsParams = {
|
||||
nodeId: string;
|
||||
transport: "relay";
|
||||
relayHandle: string;
|
||||
sendGrant: string;
|
||||
installationId: string;
|
||||
topic: string;
|
||||
environment?: unknown;
|
||||
@@ -271,17 +273,23 @@ function normalizeDirectRegistration(
|
||||
}
|
||||
|
||||
function normalizeRelayRegistration(
|
||||
record: Partial<RelayApnsRegistration> & { nodeId?: unknown; relayHandle?: unknown },
|
||||
record: Partial<RelayApnsRegistration> & {
|
||||
nodeId?: unknown;
|
||||
relayHandle?: unknown;
|
||||
sendGrant?: unknown;
|
||||
},
|
||||
): RelayApnsRegistration | null {
|
||||
if (
|
||||
typeof record.nodeId !== "string" ||
|
||||
typeof record.relayHandle !== "string" ||
|
||||
typeof record.sendGrant !== "string" ||
|
||||
typeof record.installationId !== "string"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const nodeId = normalizeNodeId(record.nodeId);
|
||||
const relayHandle = normalizeRelayHandle(record.relayHandle);
|
||||
const sendGrant = record.sendGrant.trim();
|
||||
const installationId = normalizeInstallationId(record.installationId);
|
||||
const topic = normalizeTopic(typeof record.topic === "string" ? record.topic : "");
|
||||
const environment = normalizeApnsEnvironment(record.environment);
|
||||
@@ -293,6 +301,7 @@ function normalizeRelayRegistration(
|
||||
if (
|
||||
!nodeId ||
|
||||
!relayHandle ||
|
||||
!sendGrant ||
|
||||
!installationId ||
|
||||
!topic ||
|
||||
environment !== "production" ||
|
||||
@@ -304,6 +313,7 @@ function normalizeRelayRegistration(
|
||||
nodeId,
|
||||
transport: "relay",
|
||||
relayHandle,
|
||||
sendGrant,
|
||||
installationId,
|
||||
topic,
|
||||
environment,
|
||||
@@ -393,6 +403,7 @@ export async function registerApnsRegistration(
|
||||
normalizeRelayHandle(params.relayHandle),
|
||||
"relayHandle",
|
||||
);
|
||||
const sendGrant = validateRelayIdentifier(params.sendGrant.trim(), "sendGrant");
|
||||
const installationId = validateRelayIdentifier(
|
||||
normalizeInstallationId(params.installationId),
|
||||
"installationId",
|
||||
@@ -409,6 +420,7 @@ export async function registerApnsRegistration(
|
||||
nodeId,
|
||||
transport: "relay",
|
||||
relayHandle,
|
||||
sendGrant,
|
||||
installationId,
|
||||
topic,
|
||||
environment,
|
||||
@@ -495,6 +507,7 @@ function isSameApnsRegistration(a: ApnsRegistration, b: ApnsRegistration): boole
|
||||
if (a.transport === "relay" && b.transport === "relay") {
|
||||
return (
|
||||
a.relayHandle === b.relayHandle &&
|
||||
a.sendGrant === b.sendGrant &&
|
||||
a.installationId === b.installationId &&
|
||||
a.distribution === b.distribution &&
|
||||
a.tokenDebugSuffix === b.tokenDebugSuffix
|
||||
@@ -817,6 +830,7 @@ async function sendRelayApnsPush(params: {
|
||||
}): Promise<ApnsPushResult> {
|
||||
const response = await sendApnsRelayPush({
|
||||
relayConfig: params.relayConfig,
|
||||
sendGrant: params.registration.sendGrant,
|
||||
relayHandle: params.registration.relayHandle,
|
||||
payload: params.payload,
|
||||
pushType: params.pushType,
|
||||
|
||||
Reference in New Issue
Block a user