fix: auto-recover APNs env mismatch in relay
Retry sends on BadEnvironmentKeyInToken with the opposite APNs environment, persist the corrected env, and add request/send logs for register/unregister/event delivery debugging.pull/19545/head
parent
3a5be7ad33
commit
62fae6d182
|
|
@ -9,6 +9,20 @@ import { hash } from "./hash"
|
||||||
import { delivery_log, device_registration } from "./schema.sql"
|
import { delivery_log, device_registration } from "./schema.sql"
|
||||||
import { setup } from "./setup"
|
import { setup } from "./setup"
|
||||||
|
|
||||||
|
function bad(input?: string) {
|
||||||
|
if (!input) return false
|
||||||
|
return input.includes("BadEnvironmentKeyInToken")
|
||||||
|
}
|
||||||
|
|
||||||
|
function flip(input: "sandbox" | "production") {
|
||||||
|
if (input === "sandbox") return "production"
|
||||||
|
return "sandbox"
|
||||||
|
}
|
||||||
|
|
||||||
|
function tail(input: string) {
|
||||||
|
return input.slice(-8)
|
||||||
|
}
|
||||||
|
|
||||||
const reg = z.object({
|
const reg = z.object({
|
||||||
secret: z.string().min(1),
|
secret: z.string().min(1),
|
||||||
deviceToken: z.string().min(1),
|
deviceToken: z.string().min(1),
|
||||||
|
|
@ -163,6 +177,12 @@ app.post("/v1/device/register", async (c) => {
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("[relay] register", {
|
||||||
|
token: tail(row.device_token),
|
||||||
|
env: row.apns_env,
|
||||||
|
bundle: row.bundle_id,
|
||||||
|
})
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.insert(device_registration)
|
.insert(device_registration)
|
||||||
.values(row)
|
.values(row)
|
||||||
|
|
@ -190,6 +210,10 @@ app.post("/v1/device/unregister", async (c) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("[relay] unregister", {
|
||||||
|
token: tail(check.data.deviceToken),
|
||||||
|
})
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.delete(device_registration)
|
.delete(device_registration)
|
||||||
.where(
|
.where(
|
||||||
|
|
@ -217,6 +241,11 @@ app.post("/v1/event", async (c) => {
|
||||||
|
|
||||||
const key = hash(check.data.secret)
|
const key = hash(check.data.secret)
|
||||||
const list = await db.select().from(device_registration).where(eq(device_registration.secret_hash, key))
|
const list = await db.select().from(device_registration).where(eq(device_registration.secret_hash, key))
|
||||||
|
console.log("[relay] event", {
|
||||||
|
type: check.data.eventType,
|
||||||
|
session: check.data.sessionID,
|
||||||
|
devices: list.length,
|
||||||
|
})
|
||||||
if (!list.length) {
|
if (!list.length) {
|
||||||
return c.json({
|
return c.json({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|
@ -226,21 +255,64 @@ app.post("/v1/event", async (c) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const out = await Promise.all(
|
const out = await Promise.all(
|
||||||
list.map((row) =>
|
list.map(async (row) => {
|
||||||
send({
|
const env = row.apns_env === "sandbox" ? "sandbox" : "production"
|
||||||
|
const payload = {
|
||||||
token: row.device_token,
|
token: row.device_token,
|
||||||
bundle: row.bundle_id,
|
bundle: row.bundle_id,
|
||||||
env: row.apns_env === "sandbox" ? "sandbox" : "production",
|
|
||||||
title: check.data.title ?? title(check.data.eventType),
|
title: check.data.title ?? title(check.data.eventType),
|
||||||
body: check.data.body ?? body(check.data.eventType),
|
body: check.data.body ?? body(check.data.eventType),
|
||||||
data: {
|
data: {
|
||||||
eventType: check.data.eventType,
|
eventType: check.data.eventType,
|
||||||
sessionID: check.data.sessionID,
|
sessionID: check.data.sessionID,
|
||||||
},
|
},
|
||||||
}),
|
}
|
||||||
|
const first = await send({ ...payload, env })
|
||||||
|
if (first.ok || !bad(first.error)) {
|
||||||
|
if (!first.ok) {
|
||||||
|
console.log("[relay] send:error", {
|
||||||
|
token: tail(row.device_token),
|
||||||
|
env,
|
||||||
|
error: first.error,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return first
|
||||||
|
}
|
||||||
|
|
||||||
|
const alt = flip(env)
|
||||||
|
console.log("[relay] send:retry-env", {
|
||||||
|
token: tail(row.device_token),
|
||||||
|
from: env,
|
||||||
|
to: alt,
|
||||||
|
})
|
||||||
|
const second = await send({ ...payload, env: alt })
|
||||||
|
if (!second.ok) {
|
||||||
|
console.log("[relay] send:error", {
|
||||||
|
token: tail(row.device_token),
|
||||||
|
env: alt,
|
||||||
|
error: second.error,
|
||||||
|
})
|
||||||
|
return second
|
||||||
|
}
|
||||||
|
|
||||||
|
await db
|
||||||
|
.update(device_registration)
|
||||||
|
.set({ apns_env: alt, updated_at: Date.now() })
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(device_registration.secret_hash, row.secret_hash),
|
||||||
|
eq(device_registration.device_token, row.device_token),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
console.log("[relay] send:env-updated", {
|
||||||
|
token: tail(row.device_token),
|
||||||
|
env: alt,
|
||||||
|
})
|
||||||
|
return second
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
await db.insert(delivery_log).values(
|
await db.insert(delivery_log).values(
|
||||||
out.map((item) => ({
|
out.map((item) => ({
|
||||||
|
|
@ -255,6 +327,12 @@ app.post("/v1/event", async (c) => {
|
||||||
)
|
)
|
||||||
|
|
||||||
const sent = out.filter((item) => item.ok).length
|
const sent = out.filter((item) => item.ok).length
|
||||||
|
console.log("[relay] event:done", {
|
||||||
|
type: check.data.eventType,
|
||||||
|
session: check.data.sessionID,
|
||||||
|
sent,
|
||||||
|
failed: out.length - sent,
|
||||||
|
})
|
||||||
return c.json({
|
return c.json({
|
||||||
ok: true,
|
ok: true,
|
||||||
sent,
|
sent,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue