@voltro/plugin-notifications 0.81.0 → 0.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1806 -6
- package/dist/define.d.ts +53 -0
- package/dist/define.js +15 -0
- package/dist/index.d.ts +235 -0
- package/dist/index.js +516 -215
- package/dist/rpc.d.ts +5 -0
- package/dist/rpc.js +23 -21
- package/dist/web.d.ts +2 -2
- package/package.json +10 -5
package/dist/index.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import { BASE_NAME as e, archiveDescriptor as t, clearQuietHoursDescriptor as n, inboxDescriptor as r,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { BASE_NAME as e, archiveDescriptor as t, clearQuietHoursDescriptor as n, inboxDescriptor as r, inboxFeed as i, markAllReadDescriptor as a, markReadDescriptor as o, markUnreadDescriptor as s, notificationsRpcClientImports as c, preferencesDescriptor as l, setPreferenceDescriptor as u, setQuietHoursDescriptor as d, subscribeDescriptor as f, unarchiveDescriptor as p, unreadCountDescriptor as m, unsubscribeDescriptor as ee, webPushPublicKeyDescriptor as te, webPushRpcClientImports as ne, webPushStatusDescriptor as re, webPushSubscribeDescriptor as ie, webPushUnsubscribeDescriptor as ae } from "./rpc.js";
|
|
2
|
+
import { defineNotification as h } from "./define.js";
|
|
3
|
+
import { Context as oe, Effect as g, Layer as se, Schema as _ } from "effect";
|
|
4
|
+
import { and as v, boolean as y, eq as b, generateId as x, id as S, integer as C, isNotNull as ce, isNull as w, json as T, lte as E, registerRetention as D, retentionTtlMsFromEnv as O, table as k, text as A, timestamp as j } from "@voltro/database";
|
|
5
|
+
import { definePlugin as le, pluginInstanceName as ue, publishReactivity as de } from "@voltro/protocol";
|
|
6
|
+
import { createCipheriv as M, createDecipheriv as fe, createECDH as N, createHash as pe, createHmac as me, createPrivateKey as he, randomBytes as ge, randomUUID as P, sign as F, timingSafeEqual as _e } from "node:crypto";
|
|
6
7
|
//#region src/errors.ts
|
|
7
|
-
var
|
|
8
|
+
var I = class extends _.TaggedError()("PushTokenRejected", {
|
|
8
9
|
token: _.String,
|
|
9
10
|
reason: _.String
|
|
10
|
-
}) {},
|
|
11
|
+
}) {}, ve = () => ({
|
|
11
12
|
id: "console",
|
|
12
13
|
deliver: async (e) => {
|
|
13
14
|
process.stdout.write(`[notify:${e.category}] → ${e.to}: ${e.title} — ${e.body}\n`);
|
|
14
15
|
}
|
|
15
|
-
}),
|
|
16
|
+
}), ye = (e) => ({
|
|
16
17
|
id: e.id ?? "slack",
|
|
17
18
|
deliver: async (t) => {
|
|
18
19
|
let n = e.format ? e.format(t) : { text: `*${t.title}*\n${t.body}` }, r = await fetch(e.url, {
|
|
@@ -25,7 +26,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
25
26
|
});
|
|
26
27
|
if (!r.ok) throw Error(`webhook ${e.id ?? "slack"} POST ${r.status}`);
|
|
27
28
|
}
|
|
28
|
-
}),
|
|
29
|
+
}), be = (e) => ({
|
|
29
30
|
id: "email",
|
|
30
31
|
deliver: (t) => e({
|
|
31
32
|
to: t.to,
|
|
@@ -33,20 +34,30 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
33
34
|
html: `<p>${t.body}</p>`,
|
|
34
35
|
text: t.body
|
|
35
36
|
})
|
|
36
|
-
}),
|
|
37
|
+
}), xe = (e) => ({
|
|
37
38
|
id: "sms",
|
|
38
39
|
deliver: (t) => e(t.to, `${t.title}: ${t.body}`)
|
|
39
|
-
}),
|
|
40
|
+
}), Se = (e, t) => ({
|
|
40
41
|
id: e,
|
|
41
42
|
deliver: t
|
|
42
|
-
}),
|
|
43
|
+
}), Ce = (e) => {
|
|
44
|
+
let t = typeof e?.image == "string" ? e.image : void 0, n = e?.actions, r = Array.isArray(n) ? n.filter((e) => typeof e == "object" && !!e && typeof e.id == "string" && typeof e.title == "string").map((e) => ({
|
|
45
|
+
id: e.id,
|
|
46
|
+
title: e.title
|
|
47
|
+
})) : void 0;
|
|
48
|
+
return {
|
|
49
|
+
...t === void 0 ? {} : { image: t },
|
|
50
|
+
...r !== void 0 && r.length > 0 ? { actions: r } : {}
|
|
51
|
+
};
|
|
52
|
+
}, we = (e) => {
|
|
43
53
|
let t = async (t, n, r) => {
|
|
44
54
|
let i = {
|
|
45
55
|
token: n,
|
|
46
56
|
title: t.title,
|
|
47
57
|
body: t.body,
|
|
48
58
|
...r === void 0 ? {} : { badge: r },
|
|
49
|
-
data: t.data ?? {}
|
|
59
|
+
data: t.data ?? {},
|
|
60
|
+
...Ce(t.data)
|
|
50
61
|
};
|
|
51
62
|
try {
|
|
52
63
|
return await e.transport(i), {
|
|
@@ -54,7 +65,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
54
65
|
status: "sent"
|
|
55
66
|
};
|
|
56
67
|
} catch (t) {
|
|
57
|
-
let r = t instanceof
|
|
68
|
+
let r = t instanceof I, i = r ? t.reason : t instanceof Error ? t.message : String(t);
|
|
58
69
|
if (r && e.onTokenRejected !== void 0) try {
|
|
59
70
|
await e.onTokenRejected(n, i);
|
|
60
71
|
} catch {}
|
|
@@ -74,17 +85,17 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
74
85
|
id: e.id ?? "push",
|
|
75
86
|
deliver: async (e) => {
|
|
76
87
|
let t = await n(e), r = t.filter((e) => e.status === "failed");
|
|
77
|
-
if (t.length > 0 && r.length === t.length) throw new
|
|
88
|
+
if (t.length > 0 && r.length === t.length) throw new I({
|
|
78
89
|
token: t[0].endpoint,
|
|
79
90
|
reason: r[0].error ?? "all tokens rejected"
|
|
80
91
|
});
|
|
81
92
|
},
|
|
82
93
|
deliverDetailed: n
|
|
83
94
|
};
|
|
84
|
-
},
|
|
95
|
+
}, Te = (e, t, n, r, i) => {
|
|
85
96
|
let a = n ? e.filter((e) => n.includes(e)) : [...e], o = new Set(r.filter((e) => e.subjectId === i && e.category === t && e.enabled === !1).map((e) => e.channel));
|
|
86
97
|
return a.filter((e) => !o.has(e));
|
|
87
|
-
},
|
|
98
|
+
}, Ee = () => {
|
|
88
99
|
let e = [], t = /* @__PURE__ */ new Map(), n = [], r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map(), a = [], o = (e) => `${e.subjectId}|${e.category}|${e.channel}`, s = (e, t) => `${e}|${t}`;
|
|
89
100
|
return {
|
|
90
101
|
addInbox: async (t) => {
|
|
@@ -172,7 +183,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
172
183
|
for (let e = a.length - 1; e >= 0; e--) t.has(a[e].id) && a.splice(e, 1);
|
|
173
184
|
}
|
|
174
185
|
};
|
|
175
|
-
},
|
|
186
|
+
}, L = (e) => e instanceof Date ? e.toISOString() : typeof e == "string" ? e : "", De = (e) => {
|
|
176
187
|
let t = async (t, n, r, i) => await e.query({
|
|
177
188
|
table: t,
|
|
178
189
|
predicate: n,
|
|
@@ -187,9 +198,9 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
187
198
|
title: String(e.title),
|
|
188
199
|
body: String(e.body),
|
|
189
200
|
data: e.data ?? {},
|
|
190
|
-
readAt: e.readAt == null ? null :
|
|
191
|
-
archivedAt: e.archivedAt == null ? null :
|
|
192
|
-
createdAt:
|
|
201
|
+
readAt: e.readAt == null ? null : L(e.readAt),
|
|
202
|
+
archivedAt: e.archivedAt == null ? null : L(e.archivedAt),
|
|
203
|
+
createdAt: L(e.createdAt),
|
|
193
204
|
tenantId: e.tenantId ?? null
|
|
194
205
|
}), r = (e) => ({
|
|
195
206
|
subjectId: String(e.subjectId),
|
|
@@ -202,11 +213,11 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
202
213
|
category: String(e.category),
|
|
203
214
|
channel: String(e.channel),
|
|
204
215
|
status: e.status,
|
|
205
|
-
at:
|
|
216
|
+
at: L(e.at),
|
|
206
217
|
...e.error == null ? {} : { error: String(e.error) },
|
|
207
218
|
...e.endpoint == null ? {} : { endpoint: String(e.endpoint) },
|
|
208
219
|
...e.clickToken == null ? {} : { clickToken: String(e.clickToken) },
|
|
209
|
-
...e.clickedAt == null ? {} : { clickedAt:
|
|
220
|
+
...e.clickedAt == null ? {} : { clickedAt: L(e.clickedAt) }
|
|
210
221
|
}), a = (e) => ({
|
|
211
222
|
topic: String(e.topic),
|
|
212
223
|
subjectId: String(e.subjectId),
|
|
@@ -222,7 +233,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
222
233
|
kind: e.kind === "quiet" ? "quiet" : "digest",
|
|
223
234
|
subjectId: String(e.subjectId),
|
|
224
235
|
send: e.send ?? {},
|
|
225
|
-
flushAt:
|
|
236
|
+
flushAt: L(e.flushAt)
|
|
226
237
|
});
|
|
227
238
|
return {
|
|
228
239
|
addInbox: async (t) => {
|
|
@@ -369,7 +380,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
369
380
|
for (let n of t) await e.delete("_voltro_notification_held", n);
|
|
370
381
|
}
|
|
371
382
|
};
|
|
372
|
-
},
|
|
383
|
+
}, Oe = (e) => ({
|
|
373
384
|
id: "inApp",
|
|
374
385
|
deliver: async (t) => {
|
|
375
386
|
await e.addInbox({
|
|
@@ -383,7 +394,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
383
394
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
384
395
|
});
|
|
385
396
|
}
|
|
386
|
-
}),
|
|
397
|
+
}), R = (e, t) => {
|
|
387
398
|
try {
|
|
388
399
|
let n = new Intl.DateTimeFormat("en-US", {
|
|
389
400
|
timeZone: t,
|
|
@@ -395,16 +406,16 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
395
406
|
} catch {
|
|
396
407
|
return e.getUTCHours() * 60 + e.getUTCMinutes();
|
|
397
408
|
}
|
|
398
|
-
},
|
|
409
|
+
}, ke = (e, t) => {
|
|
399
410
|
if (e.startMinute === e.endMinute) return !1;
|
|
400
|
-
let n =
|
|
411
|
+
let n = R(t, e.tz ?? "UTC");
|
|
401
412
|
return e.startMinute < e.endMinute ? n >= e.startMinute && n < e.endMinute : n >= e.startMinute || n < e.endMinute;
|
|
402
|
-
},
|
|
403
|
-
let n =
|
|
413
|
+
}, Ae = (e, t) => {
|
|
414
|
+
let n = R(t, e.tz ?? "UTC"), r = e.endMinute - n;
|
|
404
415
|
return r <= 0 && (r += 1440), new Date(t.getTime() + r * 6e4);
|
|
405
|
-
},
|
|
416
|
+
}, je = (e) => {
|
|
406
417
|
let t = new Map(e.channels.map((e) => [e.id, e])), n = e.channels.map((e) => e.id), r = e.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()), i = e.digestWindowMs ?? 0, a = async (i) => {
|
|
407
|
-
let a = await e.store.getPreferences(i.to), o =
|
|
418
|
+
let a = await e.store.getPreferences(i.to), o = Te(n, i.category, i.channels, a, i.to), s = n.filter((e) => !o.includes(e)), c = [], l = [], u = {
|
|
408
419
|
to: i.to,
|
|
409
420
|
category: i.category,
|
|
410
421
|
title: i.title,
|
|
@@ -482,7 +493,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
482
493
|
};
|
|
483
494
|
}, o = async (t) => {
|
|
484
495
|
let n = await e.store.getQuietHours(t.to), o = new Date(r());
|
|
485
|
-
if (n &&
|
|
496
|
+
if (n && ke(n, o)) return (n.policy ?? "hold") === "drop" ? {
|
|
486
497
|
delivered: [],
|
|
487
498
|
failed: [],
|
|
488
499
|
skipped: [],
|
|
@@ -492,7 +503,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
492
503
|
kind: "quiet",
|
|
493
504
|
subjectId: t.to,
|
|
494
505
|
send: t,
|
|
495
|
-
flushAt:
|
|
506
|
+
flushAt: Ae(n, o).toISOString()
|
|
496
507
|
}), {
|
|
497
508
|
delivered: [],
|
|
498
509
|
failed: [],
|
|
@@ -556,11 +567,11 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
556
567
|
let t = i.get(e.subjectId) ?? [];
|
|
557
568
|
t.push(e), i.set(e.subjectId, t);
|
|
558
569
|
}
|
|
559
|
-
for (let [e, t] of i) await a(
|
|
570
|
+
for (let [e, t] of i) await a(Me(e, t));
|
|
560
571
|
return await e.store.clearHeld(n.map((e) => e.id)), n.length;
|
|
561
572
|
}
|
|
562
573
|
};
|
|
563
|
-
},
|
|
574
|
+
}, Me = (e, t) => {
|
|
564
575
|
if (t.length === 1) return t[0].send;
|
|
565
576
|
let n = t.map((e) => ({
|
|
566
577
|
category: e.send.category,
|
|
@@ -575,82 +586,82 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
575
586
|
data: { items: n },
|
|
576
587
|
tenantId: t[0].send.tenantId ?? null
|
|
577
588
|
};
|
|
578
|
-
},
|
|
589
|
+
}, z = (e) => e.toString("base64url"), B = (e) => Buffer.from(e, "base64url"), V = (e, t) => me("sha256", e).update(t).digest(), H = (e, t, n) => V(e, Buffer.concat([t, Buffer.from([1])])).subarray(0, n), Ne = () => {
|
|
579
590
|
let e = N("prime256v1");
|
|
580
|
-
return e.generateKeys(),
|
|
581
|
-
},
|
|
591
|
+
return e.generateKeys(), z(Pe(e.getPrivateKey()));
|
|
592
|
+
}, Pe = (e) => {
|
|
582
593
|
if (e.length === 32) return e;
|
|
583
594
|
if (e.length > 32) throw Error(`P-256 scalar must be at most 32 bytes (got ${e.length})`);
|
|
584
595
|
return Buffer.concat([Buffer.alloc(32 - e.length), e]);
|
|
585
|
-
},
|
|
586
|
-
let t =
|
|
596
|
+
}, Fe = (e) => {
|
|
597
|
+
let t = B(e);
|
|
587
598
|
if (t.length !== 32) throw Error(`VOLTRO_VAPID_PRIVATE_KEY must be a base64url 32-byte P-256 scalar (got ${t.length} bytes)`);
|
|
588
599
|
return t;
|
|
589
|
-
},
|
|
590
|
-
let t =
|
|
591
|
-
return n.setPrivateKey(t),
|
|
592
|
-
},
|
|
593
|
-
let t =
|
|
600
|
+
}, U = (e) => {
|
|
601
|
+
let t = Fe(e), n = N("prime256v1");
|
|
602
|
+
return n.setPrivateKey(t), z(n.getPublicKey());
|
|
603
|
+
}, Ie = (e) => {
|
|
604
|
+
let t = Fe(e), n = N("prime256v1");
|
|
594
605
|
n.setPrivateKey(t);
|
|
595
606
|
let r = n.getPublicKey();
|
|
596
|
-
return
|
|
607
|
+
return he({
|
|
597
608
|
key: {
|
|
598
609
|
kty: "EC",
|
|
599
610
|
crv: "P-256",
|
|
600
|
-
d:
|
|
601
|
-
x:
|
|
602
|
-
y:
|
|
611
|
+
d: z(t),
|
|
612
|
+
x: z(r.subarray(1, 33)),
|
|
613
|
+
y: z(r.subarray(33, 65))
|
|
603
614
|
},
|
|
604
615
|
format: "jwk"
|
|
605
616
|
});
|
|
606
|
-
},
|
|
617
|
+
}, Le = (e, t, n, r = Date.now()) => {
|
|
607
618
|
let i = new URL(e), a = {
|
|
608
619
|
aud: `${i.protocol}//${i.host}`,
|
|
609
620
|
exp: Math.floor(r / 1e3) + 43200,
|
|
610
621
|
...n === void 0 ? {} : { sub: n }
|
|
611
|
-
}, o = `${
|
|
622
|
+
}, o = `${z(Buffer.from(JSON.stringify({
|
|
612
623
|
typ: "JWT",
|
|
613
624
|
alg: "ES256"
|
|
614
|
-
})))}.${
|
|
615
|
-
return `vapid t=${o}.${
|
|
616
|
-
key:
|
|
625
|
+
})))}.${z(Buffer.from(JSON.stringify(a)))}`;
|
|
626
|
+
return `vapid t=${o}.${z(F("sha256", Buffer.from(o), {
|
|
627
|
+
key: Ie(t),
|
|
617
628
|
dsaEncoding: "ieee-p1363"
|
|
618
|
-
}))}, k=${
|
|
619
|
-
},
|
|
620
|
-
let r =
|
|
629
|
+
}))}, k=${U(t)}`;
|
|
630
|
+
}, Re = Buffer.from("WebPush: info\0"), ze = Buffer.from("Content-Encoding: aes128gcm\0"), Be = Buffer.from("Content-Encoding: nonce\0"), Ve = 4096, He = (e, t, n = {}) => {
|
|
631
|
+
let r = B(e.p256dh), i = B(e.auth);
|
|
621
632
|
if (r.length !== 65 || r[0] !== 4) throw Error("push subscription p256dh must be a 65-byte uncompressed P-256 point");
|
|
622
633
|
if (i.length !== 16) throw Error("push subscription auth must be 16 bytes");
|
|
623
634
|
let a = N("prime256v1");
|
|
624
635
|
n.asPrivate ? a.setPrivateKey(n.asPrivate) : a.generateKeys();
|
|
625
|
-
let o = a.getPublicKey(), s =
|
|
626
|
-
|
|
636
|
+
let o = a.getPublicKey(), s = H(V(i, a.computeSecret(r)), Buffer.concat([
|
|
637
|
+
Re,
|
|
627
638
|
r,
|
|
628
639
|
o
|
|
629
|
-
]), 32), c = n.salt ??
|
|
640
|
+
]), 32), c = n.salt ?? ge(16), l = V(c, s), u = H(l, ze, 16), d = H(l, Be, 12), f = M("aes-128-gcm", u, d), p = Buffer.concat([
|
|
630
641
|
f.update(Buffer.concat([t, Buffer.from([2])])),
|
|
631
642
|
f.final(),
|
|
632
643
|
f.getAuthTag()
|
|
633
644
|
]), m = Buffer.alloc(21);
|
|
634
|
-
return c.copy(m, 0), m.writeUInt32BE(
|
|
645
|
+
return c.copy(m, 0), m.writeUInt32BE(Ve, 16), m.writeUInt8(o.length, 20), Buffer.concat([
|
|
635
646
|
m,
|
|
636
647
|
o,
|
|
637
648
|
p
|
|
638
649
|
]);
|
|
639
|
-
},
|
|
650
|
+
}, Ue = (e, t) => {
|
|
640
651
|
let n = e.subarray(0, 16), r = e.readUInt8(20), i = e.subarray(21, 21 + r), a = e.subarray(21 + r), o = N("prime256v1");
|
|
641
652
|
o.setPrivateKey(t.uaPrivate);
|
|
642
|
-
let s = o.getPublicKey(), c = o.computeSecret(i), l =
|
|
643
|
-
|
|
653
|
+
let s = o.getPublicKey(), c = o.computeSecret(i), l = V(n, H(V(t.authSecret, c), Buffer.concat([
|
|
654
|
+
Re,
|
|
644
655
|
s,
|
|
645
656
|
i
|
|
646
|
-
]), 32)), u =
|
|
657
|
+
]), 32)), u = H(l, ze, 16), d = H(l, Be, 12), f = fe("aes-128-gcm", u, d);
|
|
647
658
|
f.setAuthTag(a.subarray(a.length - 16));
|
|
648
659
|
let p = Buffer.concat([f.update(a.subarray(0, a.length - 16)), f.final()]), m = p.length - 1;
|
|
649
660
|
for (; m >= 0 && p[m] === 0;) m--;
|
|
650
661
|
if (p[m] !== 2) throw Error("web-push record missing the last-record delimiter");
|
|
651
662
|
return p.subarray(0, m);
|
|
652
|
-
},
|
|
653
|
-
let r =
|
|
663
|
+
}, We = async (e, t, n) => {
|
|
664
|
+
let r = He(e.keys, t), i = n.fetchImpl ?? fetch, a;
|
|
654
665
|
try {
|
|
655
666
|
a = await i(e.endpoint, {
|
|
656
667
|
method: "POST",
|
|
@@ -659,7 +670,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
659
670
|
"content-type": "application/octet-stream",
|
|
660
671
|
ttl: String(n.ttl ?? 86400),
|
|
661
672
|
urgency: "normal",
|
|
662
|
-
authorization:
|
|
673
|
+
authorization: Le(e.endpoint, n.privateKey, n.contact)
|
|
663
674
|
},
|
|
664
675
|
body: new Uint8Array(r)
|
|
665
676
|
});
|
|
@@ -684,10 +695,10 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
684
695
|
status: a.status,
|
|
685
696
|
detail: o.slice(0, 200)
|
|
686
697
|
};
|
|
687
|
-
},
|
|
698
|
+
}, Ge = (e, t) => {
|
|
688
699
|
let n = Buffer.from(e), r = Buffer.from(t);
|
|
689
|
-
return n.length === r.length &&
|
|
690
|
-
},
|
|
700
|
+
return n.length === r.length && _e(n, r);
|
|
701
|
+
}, Ke = k("_voltro_notification_push_subscriptions", {
|
|
691
702
|
id: S({ prefix: "notifpush" }),
|
|
692
703
|
subjectId: A(),
|
|
693
704
|
endpointHash: A(),
|
|
@@ -697,7 +708,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
697
708
|
ua: A().nullable(),
|
|
698
709
|
createdAt: j().default("now"),
|
|
699
710
|
tenantId: A().nullable()
|
|
700
|
-
}).unique("byPushEndpointHash", ["endpointHash"]).index("byPushSubject", ["subjectId"]),
|
|
711
|
+
}).unique("byPushEndpointHash", ["endpointHash"]).index("byPushSubject", ["subjectId"]), W = (e) => pe("sha256").update(e).digest("hex"), G = () => {
|
|
701
712
|
let e = /* @__PURE__ */ new Map();
|
|
702
713
|
return {
|
|
703
714
|
upsert: async (t) => {
|
|
@@ -713,9 +724,9 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
713
724
|
},
|
|
714
725
|
countForSubject: async (t) => [...e.values()].filter((e) => e.subjectId === t).length
|
|
715
726
|
};
|
|
716
|
-
},
|
|
727
|
+
}, K = "_voltro_notification_push_subscriptions", qe = (e) => {
|
|
717
728
|
let t = async (t, n) => e.query({
|
|
718
|
-
table:
|
|
729
|
+
table: K,
|
|
719
730
|
predicate: t,
|
|
720
731
|
order: [],
|
|
721
732
|
take: n,
|
|
@@ -735,8 +746,8 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
735
746
|
let n = x({
|
|
736
747
|
kind: "typeid",
|
|
737
748
|
prefix: "notifpush"
|
|
738
|
-
},
|
|
739
|
-
await e.upsert(
|
|
749
|
+
}, K);
|
|
750
|
+
await e.upsert(K, {
|
|
740
751
|
id: n,
|
|
741
752
|
subjectId: t.subjectId,
|
|
742
753
|
endpointHash: t.endpointHash,
|
|
@@ -759,31 +770,31 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
759
770
|
listForSubject: async (e) => (await t(b("subjectId", e))).map(n),
|
|
760
771
|
removeForSubject: async (n, r) => {
|
|
761
772
|
let i = await t(v(b("endpointHash", n), b("subjectId", r)), 1);
|
|
762
|
-
return i[0] ? (await e.delete(
|
|
773
|
+
return i[0] ? (await e.delete(K, String(i[0].id)), !0) : !1;
|
|
763
774
|
},
|
|
764
775
|
removeByHash: async (n) => {
|
|
765
776
|
let r = await t(b("endpointHash", n), 1);
|
|
766
|
-
r[0] && await e.delete(
|
|
777
|
+
r[0] && await e.delete(K, String(r[0].id));
|
|
767
778
|
},
|
|
768
779
|
countForSubject: async (e) => (await t(b("subjectId", e))).length
|
|
769
780
|
};
|
|
770
|
-
},
|
|
781
|
+
}, Je = Symbol.for("@voltro/plugin-notifications:web-push"), q = () => {
|
|
771
782
|
let e = globalThis;
|
|
772
|
-
return e[
|
|
773
|
-
store:
|
|
783
|
+
return e[Je] ??= {
|
|
784
|
+
store: G(),
|
|
774
785
|
bound: !1
|
|
775
786
|
};
|
|
776
|
-
},
|
|
777
|
-
let t =
|
|
778
|
-
t.store =
|
|
779
|
-
},
|
|
780
|
-
let e =
|
|
781
|
-
e.store =
|
|
782
|
-
},
|
|
787
|
+
}, Ye = (e) => {
|
|
788
|
+
let t = q();
|
|
789
|
+
t.store = qe(e), t.bound = !0;
|
|
790
|
+
}, Xe = () => {
|
|
791
|
+
let e = q();
|
|
792
|
+
e.store = G(), e.bound = !1;
|
|
793
|
+
}, J = (e) => {
|
|
783
794
|
let t = e ?? process.env.VOLTRO_VAPID_PRIVATE_KEY;
|
|
784
795
|
if (t === void 0 || t.trim() === "") throw Error("web push needs VOLTRO_VAPID_PRIVATE_KEY (a base64url P-256 scalar). `voltro dev` mints one into .env.local; for production generate one and set it in the deployment environment. There is deliberately no default.");
|
|
785
796
|
return t;
|
|
786
|
-
},
|
|
797
|
+
}, Y = 3800, Ze = (e, t) => {
|
|
787
798
|
let n = typeof e.data?.url == "string" ? e.data.url : void 0, r = {
|
|
788
799
|
title: e.title,
|
|
789
800
|
body: e.body,
|
|
@@ -792,44 +803,44 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
792
803
|
...n === void 0 ? {} : { url: n },
|
|
793
804
|
...e.data === void 0 ? {} : { data: e.data }
|
|
794
805
|
}, i = Buffer.from(JSON.stringify(r));
|
|
795
|
-
if (i.length <=
|
|
806
|
+
if (i.length <= Y) return i;
|
|
796
807
|
let { data: a, ...o } = r;
|
|
797
|
-
if (i = Buffer.from(JSON.stringify(o)), i.length <=
|
|
798
|
-
let s = i.length - Buffer.byteLength(o.body), c = Math.max(0,
|
|
808
|
+
if (i = Buffer.from(JSON.stringify(o)), i.length <= Y) return i;
|
|
809
|
+
let s = i.length - Buffer.byteLength(o.body), c = Math.max(0, Y - s - Buffer.byteLength("…")), l = Buffer.from(o.body).subarray(0, c).toString("utf8").replace(/�+$/u, "");
|
|
799
810
|
return Buffer.from(JSON.stringify({
|
|
800
811
|
...o,
|
|
801
812
|
body: `${l}…`
|
|
802
813
|
}));
|
|
803
|
-
},
|
|
804
|
-
let e =
|
|
805
|
-
|
|
806
|
-
},
|
|
814
|
+
}, Qe = (e = {}) => (q().channelOptions = e, et(e)), $e = () => {
|
|
815
|
+
let e = q().channelOptions;
|
|
816
|
+
U(J(e?.privateKey));
|
|
817
|
+
}, et = (e) => ({
|
|
807
818
|
id: e.id ?? "webPush",
|
|
808
819
|
deliver: async (t) => {
|
|
809
|
-
let n = await
|
|
820
|
+
let n = await tt(t, e), r = n.filter((e) => e.status === "failed");
|
|
810
821
|
if (n.length > 0 && r.length === n.length) throw Error(`web push failed for all ${r.length} endpoint(s): ${r[0].error ?? "unknown"}`);
|
|
811
822
|
},
|
|
812
|
-
deliverDetailed: (t) =>
|
|
813
|
-
}),
|
|
814
|
-
let n =
|
|
823
|
+
deliverDetailed: (t) => tt(t, e)
|
|
824
|
+
}), tt = async (e, t) => {
|
|
825
|
+
let n = J(t.privateKey), r = await q().store.listForSubject(e.to), i = [];
|
|
815
826
|
for (let a of r) {
|
|
816
|
-
let r =
|
|
827
|
+
let r = ge(16).toString("base64url"), o = {
|
|
817
828
|
privateKey: n,
|
|
818
829
|
...t.contact === void 0 ? {} : { contact: t.contact },
|
|
819
830
|
...t.ttl === void 0 ? {} : { ttl: t.ttl },
|
|
820
831
|
...t.fetchImpl === void 0 ? {} : { fetchImpl: t.fetchImpl }
|
|
821
|
-
}, s = await
|
|
832
|
+
}, s = await We({
|
|
822
833
|
endpoint: a.endpoint,
|
|
823
834
|
keys: {
|
|
824
835
|
p256dh: a.p256dh,
|
|
825
836
|
auth: a.auth
|
|
826
837
|
}
|
|
827
|
-
},
|
|
838
|
+
}, Ze(e, r), o);
|
|
828
839
|
s.kind === "delivered" ? i.push({
|
|
829
840
|
endpoint: a.endpoint,
|
|
830
841
|
status: "sent",
|
|
831
842
|
clickToken: r
|
|
832
|
-
}) : s.kind === "gone" ? (await
|
|
843
|
+
}) : s.kind === "gone" ? (await q().store.removeByHash(a.endpointHash), i.push({
|
|
833
844
|
endpoint: a.endpoint,
|
|
834
845
|
status: "failed",
|
|
835
846
|
error: `endpoint gone (${s.status}) — pruned`,
|
|
@@ -841,7 +852,277 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
841
852
|
});
|
|
842
853
|
}
|
|
843
854
|
return i;
|
|
844
|
-
},
|
|
855
|
+
}, nt = (e) => U(J(e)), rt = (e) => {
|
|
856
|
+
if (typeof e == "object" && e) {
|
|
857
|
+
let t = e;
|
|
858
|
+
if (typeof t.userId == "string") return [t.userId];
|
|
859
|
+
if (Array.isArray(t.userIds)) return t.userIds.filter((e) => typeof e == "string");
|
|
860
|
+
}
|
|
861
|
+
throw Error("sendNotification: pass `recipients` — the audience has neither `userId` nor `userIds`");
|
|
862
|
+
}, it = async (e, t, n) => {
|
|
863
|
+
let r = _.decodeUnknownSync(t.audience)(n.audience), i = _.decodeUnknownSync(t.payload)(n.payload), a = n.recipients ? await n.recipients(r) : rt(r), o = [];
|
|
864
|
+
for (let r of a) {
|
|
865
|
+
let a = (n.localeFor ? await n.localeFor(r) : null) ?? n.defaultLocale ?? "en", s = await e.send(at(t, i, r, a, n.tenantId));
|
|
866
|
+
o.push({
|
|
867
|
+
recipientId: r,
|
|
868
|
+
locale: a,
|
|
869
|
+
result: s
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
return o;
|
|
873
|
+
}, at = (e, t, n, r, i) => {
|
|
874
|
+
let a = e.render(t, {
|
|
875
|
+
locale: r,
|
|
876
|
+
recipientId: n
|
|
877
|
+
});
|
|
878
|
+
return {
|
|
879
|
+
to: n,
|
|
880
|
+
category: e.category,
|
|
881
|
+
title: a.title,
|
|
882
|
+
body: a.body,
|
|
883
|
+
data: {
|
|
884
|
+
...a.data,
|
|
885
|
+
notification: e.name,
|
|
886
|
+
...a.image === void 0 ? {} : { image: a.image },
|
|
887
|
+
...a.actions === void 0 ? {} : { actions: a.actions }
|
|
888
|
+
},
|
|
889
|
+
...e.channels === void 0 ? {} : { channels: e.channels },
|
|
890
|
+
...i === void 0 ? {} : { tenantId: i }
|
|
891
|
+
};
|
|
892
|
+
}, X = "voltro.notification.send", ot = async (e, t, n) => {
|
|
893
|
+
let r = _.decodeUnknownSync(t.audience)(n.audience), i = _.is(t.payload)(n.payload) ? _.encodeUnknownSync(t.payload)(n.payload) : (_.decodeUnknownSync(t.payload)(n.payload), n.payload), a = n.recipients ? await n.recipients(r) : rt(r), o = [];
|
|
894
|
+
for (let r of a) {
|
|
895
|
+
let a = (n.localeFor ? await n.localeFor(r) : null) ?? n.defaultLocale ?? "en", s = {
|
|
896
|
+
notification: t.name,
|
|
897
|
+
recipientId: r,
|
|
898
|
+
locale: a,
|
|
899
|
+
payload: i,
|
|
900
|
+
tenantId: n.tenantId ?? null
|
|
901
|
+
}, c = await e.enqueue(X, { ...s }, {
|
|
902
|
+
...n.idempotencyKey === void 0 ? {} : { idempotencyKey: `${t.name}:${r}:${n.idempotencyKey}` },
|
|
903
|
+
...n.maxAttempts === void 0 ? {} : { maxAttempts: n.maxAttempts },
|
|
904
|
+
...n.delayMs === void 0 ? {} : { delayMs: n.delayMs }
|
|
905
|
+
});
|
|
906
|
+
o.push({
|
|
907
|
+
recipientId: r,
|
|
908
|
+
locale: a,
|
|
909
|
+
outboxId: c
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
return o;
|
|
913
|
+
}, st = (e) => typeof e.notification == "string" && typeof e.recipientId == "string" && typeof e.locale == "string", ct = (e) => {
|
|
914
|
+
let t = new Map(e.notifications.map((e) => [e.name, e]));
|
|
915
|
+
return {
|
|
916
|
+
effect: X,
|
|
917
|
+
maxAttempts: e.maxAttempts ?? 5,
|
|
918
|
+
handler: async ({ payload: n }) => {
|
|
919
|
+
if (!st(n)) throw Error(`${X}: row payload needs notification, recipientId and locale — got keys ${Object.keys(n).join(", ") || "(none)"}`);
|
|
920
|
+
let r = t.get(n.notification);
|
|
921
|
+
if (r === void 0) throw Error(`${X}: no descriptor named ${JSON.stringify(n.notification)} in this handler — list it in notificationOutboxHandler({ notifications })`);
|
|
922
|
+
let i = e.service();
|
|
923
|
+
if (i === void 0) throw Error(`${X}: the notification service is not available in this process`);
|
|
924
|
+
let a = _.decodeUnknownSync(r.payload)(n.payload);
|
|
925
|
+
return i.send(at(r, a, n.recipientId, n.locale, n.tenantId));
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
}, lt = {
|
|
929
|
+
production: "https://api.push.apple.com",
|
|
930
|
+
sandbox: "https://api.sandbox.push.apple.com"
|
|
931
|
+
}, ut = 3e6, dt = /* @__PURE__ */ new Set([
|
|
932
|
+
"BadDeviceToken",
|
|
933
|
+
"Unregistered",
|
|
934
|
+
"DeviceTokenNotForTopic",
|
|
935
|
+
"ExpiredToken"
|
|
936
|
+
]), Z = (e) => Buffer.from(e).toString("base64url"), ft = (e, t) => {
|
|
937
|
+
let n = Z(JSON.stringify({
|
|
938
|
+
alg: "ES256",
|
|
939
|
+
kid: e.keyId
|
|
940
|
+
})), r = Z(JSON.stringify({
|
|
941
|
+
iss: e.teamId,
|
|
942
|
+
iat: Math.floor(t / 1e3)
|
|
943
|
+
}));
|
|
944
|
+
return `${n}.${r}.${Z(F("sha256", Buffer.from(`${n}.${r}`), {
|
|
945
|
+
key: e.privateKey,
|
|
946
|
+
dsaEncoding: "ieee-p1363"
|
|
947
|
+
}))}`;
|
|
948
|
+
}, pt = (e) => ({
|
|
949
|
+
aps: {
|
|
950
|
+
alert: {
|
|
951
|
+
title: e.title,
|
|
952
|
+
body: e.body
|
|
953
|
+
},
|
|
954
|
+
...e.badge === void 0 ? {} : { badge: e.badge },
|
|
955
|
+
sound: "default",
|
|
956
|
+
...e.image === void 0 ? {} : { "mutable-content": 1 },
|
|
957
|
+
...e.actions !== void 0 && typeof e.data.notification == "string" ? { category: e.data.notification } : {}
|
|
958
|
+
},
|
|
959
|
+
...e.data
|
|
960
|
+
}), mt = async ({ url: e, headers: t, body: n }) => {
|
|
961
|
+
let r = await import("node:http2"), i = new URL(e), a = r.connect(i.origin);
|
|
962
|
+
try {
|
|
963
|
+
return await new Promise((e, r) => {
|
|
964
|
+
a.once("error", r);
|
|
965
|
+
let o = a.request({
|
|
966
|
+
":method": "POST",
|
|
967
|
+
":path": i.pathname,
|
|
968
|
+
...t
|
|
969
|
+
}), s = 0, c = [];
|
|
970
|
+
o.on("response", (e) => {
|
|
971
|
+
s = Number(e[":status"] ?? 0);
|
|
972
|
+
}), o.on("data", (e) => c.push(e)), o.on("end", () => e({
|
|
973
|
+
status: s,
|
|
974
|
+
body: Buffer.concat(c).toString("utf8")
|
|
975
|
+
})), o.on("error", r), o.end(n);
|
|
976
|
+
});
|
|
977
|
+
} finally {
|
|
978
|
+
a.close();
|
|
979
|
+
}
|
|
980
|
+
}, ht = (e) => {
|
|
981
|
+
let t = [
|
|
982
|
+
"keyId",
|
|
983
|
+
"teamId",
|
|
984
|
+
"privateKey",
|
|
985
|
+
"bundleId"
|
|
986
|
+
].filter((t) => !e[t]);
|
|
987
|
+
if (t.length > 0) throw Error(`apnsTransport: ${t.join(", ")} required — supply APNS_KEY_ID, APNS_TEAM_ID, APNS_PRIVATE_KEY (the .p8) and APNS_BUNDLE_ID from the environment`);
|
|
988
|
+
let n = e.request ?? mt, r = e.now ?? Date.now, i = lt[e.environment ?? "production"], a = null, o = () => {
|
|
989
|
+
let t = r();
|
|
990
|
+
return (a === null || t - a.at > ut) && (a = {
|
|
991
|
+
token: ft(e, t),
|
|
992
|
+
at: t
|
|
993
|
+
}), a.token;
|
|
994
|
+
};
|
|
995
|
+
return async (t) => {
|
|
996
|
+
let r = await n({
|
|
997
|
+
url: `${i}/3/device/${encodeURIComponent(t.token)}`,
|
|
998
|
+
headers: {
|
|
999
|
+
authorization: `bearer ${o()}`,
|
|
1000
|
+
"apns-topic": e.bundleId,
|
|
1001
|
+
"apns-push-type": "alert",
|
|
1002
|
+
"apns-priority": "10",
|
|
1003
|
+
"content-type": "application/json"
|
|
1004
|
+
},
|
|
1005
|
+
body: JSON.stringify(pt(t))
|
|
1006
|
+
});
|
|
1007
|
+
if (r.status === 200) return;
|
|
1008
|
+
let a = "";
|
|
1009
|
+
try {
|
|
1010
|
+
a = String(JSON.parse(r.body).reason ?? "");
|
|
1011
|
+
} catch {
|
|
1012
|
+
a = "";
|
|
1013
|
+
}
|
|
1014
|
+
throw r.status === 410 || dt.has(a) ? new I({
|
|
1015
|
+
token: t.token,
|
|
1016
|
+
reason: a || "Unregistered"
|
|
1017
|
+
}) : Error(`apns: HTTP ${r.status}${a ? ` — ${a}` : ""}`);
|
|
1018
|
+
};
|
|
1019
|
+
}, gt = "https://www.googleapis.com/auth/firebase.messaging", _t = "https://oauth2.googleapis.com/token", vt = (e) => `https://fcm.googleapis.com/v1/projects/${encodeURIComponent(e)}/messages:send`, yt = /* @__PURE__ */ new Set(["UNREGISTERED", "NOT_FOUND"]), Q = (e) => Buffer.from(e).toString("base64url"), bt = (e) => {
|
|
1020
|
+
let t;
|
|
1021
|
+
if (typeof e == "string") try {
|
|
1022
|
+
t = JSON.parse(e);
|
|
1023
|
+
} catch {
|
|
1024
|
+
throw Error("fcmTransport: FCM_SERVICE_ACCOUNT is not the JSON Google issued");
|
|
1025
|
+
}
|
|
1026
|
+
else t = e;
|
|
1027
|
+
let n = [
|
|
1028
|
+
"project_id",
|
|
1029
|
+
"client_email",
|
|
1030
|
+
"private_key"
|
|
1031
|
+
].filter((e) => typeof t[e] != "string" || t[e].length === 0);
|
|
1032
|
+
if (n.length > 0) throw Error(`fcmTransport: service account is missing ${n.join(", ")} — supply FCM_SERVICE_ACCOUNT from the environment`);
|
|
1033
|
+
return t;
|
|
1034
|
+
}, xt = (e, t) => {
|
|
1035
|
+
let n = Math.floor(t / 1e3), r = Q(JSON.stringify({
|
|
1036
|
+
alg: "RS256",
|
|
1037
|
+
typ: "JWT"
|
|
1038
|
+
})), i = Q(JSON.stringify({
|
|
1039
|
+
iss: e.client_email,
|
|
1040
|
+
scope: gt,
|
|
1041
|
+
aud: e.token_uri ?? "https://oauth2.googleapis.com/token",
|
|
1042
|
+
iat: n,
|
|
1043
|
+
exp: n + 3600
|
|
1044
|
+
}));
|
|
1045
|
+
return `${r}.${i}.${Q(F("sha256", Buffer.from(`${r}.${i}`), e.private_key))}`;
|
|
1046
|
+
}, St = (e) => ({ message: {
|
|
1047
|
+
token: e.token,
|
|
1048
|
+
notification: {
|
|
1049
|
+
title: e.title,
|
|
1050
|
+
body: e.body,
|
|
1051
|
+
...e.image === void 0 ? {} : { image: e.image }
|
|
1052
|
+
},
|
|
1053
|
+
data: Object.fromEntries(Object.entries(e.data).map(([e, t]) => [e, typeof t == "string" ? t : JSON.stringify(t)])),
|
|
1054
|
+
...e.badge !== void 0 || e.image !== void 0 ? { apns: {
|
|
1055
|
+
payload: { aps: {
|
|
1056
|
+
...e.badge === void 0 ? {} : { badge: e.badge },
|
|
1057
|
+
...e.image === void 0 ? {} : { "mutable-content": 1 }
|
|
1058
|
+
} },
|
|
1059
|
+
...e.image === void 0 ? {} : { fcm_options: { image: e.image } }
|
|
1060
|
+
} } : {}
|
|
1061
|
+
} }), Ct = (e) => {
|
|
1062
|
+
let t = bt(e.serviceAccount), n = e.fetch ?? fetch, r = e.now ?? Date.now, i = e.endpoint ?? vt(t.project_id), a = null, o = async () => {
|
|
1063
|
+
let e = r();
|
|
1064
|
+
if (a !== null && a.expiresAt - 6e4 > e) return a.token;
|
|
1065
|
+
let i = await n(t.token_uri ?? "https://oauth2.googleapis.com/token", {
|
|
1066
|
+
method: "POST",
|
|
1067
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
1068
|
+
body: new URLSearchParams({
|
|
1069
|
+
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
1070
|
+
assertion: xt(t, e)
|
|
1071
|
+
}).toString()
|
|
1072
|
+
}), o = await i.json().catch(() => ({}));
|
|
1073
|
+
if (!i.ok || typeof o.access_token != "string") throw Error(`fcm: token exchange HTTP ${i.status}${o.error ? ` — ${o.error}` : ""}`);
|
|
1074
|
+
return a = {
|
|
1075
|
+
token: o.access_token,
|
|
1076
|
+
expiresAt: e + (o.expires_in ?? 3600) * 1e3
|
|
1077
|
+
}, a.token;
|
|
1078
|
+
};
|
|
1079
|
+
return async (e) => {
|
|
1080
|
+
let t = await n(i, {
|
|
1081
|
+
method: "POST",
|
|
1082
|
+
headers: {
|
|
1083
|
+
authorization: `Bearer ${await o()}`,
|
|
1084
|
+
"content-type": "application/json"
|
|
1085
|
+
},
|
|
1086
|
+
body: JSON.stringify(St(e))
|
|
1087
|
+
});
|
|
1088
|
+
if (t.ok) return;
|
|
1089
|
+
let r = await t.json().catch(() => ({})), a = r.error?.details?.find((e) => typeof e.errorCode == "string")?.errorCode ?? r.error?.status ?? "";
|
|
1090
|
+
throw t.status === 404 || yt.has(a) ? new I({
|
|
1091
|
+
token: e.token,
|
|
1092
|
+
reason: a || "UNREGISTERED"
|
|
1093
|
+
}) : Error(`fcm: HTTP ${t.status}${a ? ` — ${a}` : ""}${r.error?.message ? ` — ${r.error.message}` : ""}`);
|
|
1094
|
+
};
|
|
1095
|
+
}, wt = "https://exp.host/--/api/v2/push/send", Tt = (e) => /^Expo(nent)?PushToken\[[^\]]+\]$/.test(e), Et = (e = {}) => async (t) => {
|
|
1096
|
+
if (!Tt(t.token)) throw new I({
|
|
1097
|
+
token: t.token,
|
|
1098
|
+
reason: "not an Expo push token"
|
|
1099
|
+
});
|
|
1100
|
+
let n = await (e.fetch ?? fetch)(e.endpoint ?? "https://exp.host/--/api/v2/push/send", {
|
|
1101
|
+
method: "POST",
|
|
1102
|
+
headers: {
|
|
1103
|
+
"content-type": "application/json",
|
|
1104
|
+
accept: "application/json",
|
|
1105
|
+
...e.accessToken ? { authorization: `Bearer ${e.accessToken}` } : {}
|
|
1106
|
+
},
|
|
1107
|
+
body: JSON.stringify({
|
|
1108
|
+
to: t.token,
|
|
1109
|
+
title: t.title,
|
|
1110
|
+
body: t.body,
|
|
1111
|
+
data: t.data,
|
|
1112
|
+
...t.badge === void 0 ? {} : { badge: t.badge },
|
|
1113
|
+
...t.image === void 0 ? {} : { richContent: { image: t.image } },
|
|
1114
|
+
...t.actions !== void 0 && typeof t.data.notification == "string" ? { categoryId: t.data.notification } : {}
|
|
1115
|
+
})
|
|
1116
|
+
}), r = await n.json().catch(() => ({}));
|
|
1117
|
+
if (!n.ok) throw Error(`expo push: HTTP ${n.status}${r.errors?.[0]?.message ? ` — ${r.errors[0].message}` : ""}`);
|
|
1118
|
+
let i = Array.isArray(r.data) ? r.data[0] : r.data;
|
|
1119
|
+
if (!i || i.status === "ok") return;
|
|
1120
|
+
let a = i.details?.error;
|
|
1121
|
+
throw a === "DeviceNotRegistered" || a === "InvalidCredentials" ? new I({
|
|
1122
|
+
token: t.token,
|
|
1123
|
+
reason: a
|
|
1124
|
+
}) : Error(`expo push: ${a ?? "error"}${i.message ? ` — ${i.message}` : ""}`);
|
|
1125
|
+
}, Dt = k("_voltro_notification_inbox", {
|
|
845
1126
|
id: S({ prefix: "notif" }),
|
|
846
1127
|
subjectId: A(),
|
|
847
1128
|
category: A(),
|
|
@@ -852,7 +1133,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
852
1133
|
archivedAt: j().nullable(),
|
|
853
1134
|
createdAt: j().default("now"),
|
|
854
1135
|
tenantId: A().nullable()
|
|
855
|
-
}).renamedFrom("notification_inbox").index("byInboxSubject", ["subjectId"]).index("byInboxSubjectCreatedAt", ["subjectId", "createdAt"]).index("byInboxSubjectUnread", ["subjectId"], { where: "\"readAt\" IS NULL" }),
|
|
1136
|
+
}).renamedFrom("notification_inbox").index("byInboxSubject", ["subjectId"]).index("byInboxSubjectCreatedAt", ["subjectId", "createdAt"]).index("byInboxSubjectUnread", ["subjectId"], { where: "\"readAt\" IS NULL" }), Ot = k("_voltro_notification_preferences", {
|
|
856
1137
|
id: S({ prefix: "notifpref" }),
|
|
857
1138
|
subjectId: A(),
|
|
858
1139
|
category: A(),
|
|
@@ -862,7 +1143,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
862
1143
|
"subjectId",
|
|
863
1144
|
"category",
|
|
864
1145
|
"channel"
|
|
865
|
-
]),
|
|
1146
|
+
]), kt = k("_voltro_notification_deliveries", {
|
|
866
1147
|
id: S({ prefix: "notifdlv" }),
|
|
867
1148
|
recipient: A(),
|
|
868
1149
|
category: A(),
|
|
@@ -873,33 +1154,52 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
873
1154
|
endpoint: A().nullable(),
|
|
874
1155
|
clickToken: A().nullable(),
|
|
875
1156
|
clickedAt: j().nullable()
|
|
876
|
-
}).renamedFrom("notification_deliveries").index("byDeliveriesAt", ["at"]).index("byDeliveriesClickToken", ["clickToken"]),
|
|
1157
|
+
}).renamedFrom("notification_deliveries").index("byDeliveriesAt", ["at"]).index("byDeliveriesClickToken", ["clickToken"]), At = k("_voltro_notification_topic_subscriptions", {
|
|
877
1158
|
id: S({ prefix: "notiftsub" }),
|
|
878
1159
|
topic: A(),
|
|
879
1160
|
subjectId: A(),
|
|
880
1161
|
tenantId: A().nullable()
|
|
881
|
-
}).renamedFrom("notification_topic_subscriptions").unique("byTopicSubject", ["topic", "subjectId"]).index("byTopic", ["topic"]),
|
|
1162
|
+
}).renamedFrom("notification_topic_subscriptions").unique("byTopicSubject", ["topic", "subjectId"]).index("byTopic", ["topic"]), jt = k("_voltro_notification_quiet_hours", {
|
|
882
1163
|
id: S({ prefix: "notifqh" }),
|
|
883
1164
|
subjectId: A(),
|
|
884
1165
|
startMinute: C(),
|
|
885
1166
|
endMinute: C(),
|
|
886
1167
|
tz: A().default("UTC"),
|
|
887
1168
|
policy: A().default("hold")
|
|
888
|
-
}).renamedFrom("notification_quiet_hours").unique("byQuietHoursSubject", ["subjectId"]),
|
|
1169
|
+
}).renamedFrom("notification_quiet_hours").unique("byQuietHoursSubject", ["subjectId"]), Mt = k("_voltro_notification_held", {
|
|
889
1170
|
id: S({ prefix: "notifheld" }),
|
|
890
1171
|
kind: A(),
|
|
891
1172
|
subjectId: A(),
|
|
892
1173
|
send: T(),
|
|
893
1174
|
flushAt: j()
|
|
894
|
-
}).renamedFrom("notification_held").index("byHeldFlushAt", ["flushAt"]).index("byHeldSubjectKind", ["subjectId", "kind"]),
|
|
1175
|
+
}).renamedFrom("notification_held").index("byHeldFlushAt", ["flushAt"]).index("byHeldSubjectKind", ["subjectId", "kind"]), $ = class extends oe.Tag("@voltro/plugin-notifications/NotificationService")() {}, Nt = (e) => async (t) => {
|
|
895
1176
|
if (e !== void 0) try {
|
|
896
1177
|
let n = await e(t);
|
|
897
1178
|
if (typeof n == "string" && n !== "") return n;
|
|
898
1179
|
} catch {}
|
|
899
1180
|
return t.request.subject.id ?? "anonymous";
|
|
900
|
-
},
|
|
901
|
-
|
|
902
|
-
|
|
1181
|
+
}, Pt = (e) => e.request.subject.tenantId ?? null, Ft = e, It = { collects: [
|
|
1182
|
+
{
|
|
1183
|
+
category: "device-id",
|
|
1184
|
+
linked: !0,
|
|
1185
|
+
tracking: !1,
|
|
1186
|
+
purposes: ["app-functionality"]
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
category: "identifiers",
|
|
1190
|
+
linked: !0,
|
|
1191
|
+
tracking: !1,
|
|
1192
|
+
purposes: ["app-functionality"]
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
category: "other-diagnostic-data",
|
|
1196
|
+
linked: !1,
|
|
1197
|
+
tracking: !1,
|
|
1198
|
+
purposes: ["app-functionality"]
|
|
1199
|
+
}
|
|
1200
|
+
] }, Lt = (e = {}) => {
|
|
1201
|
+
let h = Nt(e.resolveSubjectId), oe = ue({
|
|
1202
|
+
base: Ft,
|
|
903
1203
|
alias: e.alias,
|
|
904
1204
|
instance: e.name
|
|
905
1205
|
});
|
|
@@ -915,7 +1215,7 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
915
1215
|
timeColumn: "createdAt",
|
|
916
1216
|
ttlMs: O(process.env.VOLTRO_NOTIFICATION_INBOX_READ_TTL_HOURS, 4320),
|
|
917
1217
|
envVar: "VOLTRO_NOTIFICATION_INBOX_READ_TTL_HOURS",
|
|
918
|
-
where:
|
|
1218
|
+
where: ce("readAt")
|
|
919
1219
|
}), D({
|
|
920
1220
|
source: "plugin",
|
|
921
1221
|
table: "_voltro_notification_held",
|
|
@@ -923,80 +1223,80 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
923
1223
|
ttlMs: O(process.env.VOLTRO_NOTIFICATION_HELD_TTL_HOURS, 168),
|
|
924
1224
|
envVar: "VOLTRO_NOTIFICATION_HELD_TTL_HOURS"
|
|
925
1225
|
});
|
|
926
|
-
let
|
|
927
|
-
addInbox: (e) =>
|
|
928
|
-
listInbox: (e, t) =>
|
|
929
|
-
markRead: (e, t) =>
|
|
930
|
-
markUnread: (e, t) =>
|
|
931
|
-
markAllRead: (e) =>
|
|
932
|
-
setArchived: (e, t, n) =>
|
|
933
|
-
unreadCount: (e) =>
|
|
934
|
-
getPreferences: (e) =>
|
|
935
|
-
setPreference: (e) =>
|
|
936
|
-
recordDelivery: (e) =>
|
|
937
|
-
listDeliveries: (e) =>
|
|
938
|
-
markClicked: (e) =>
|
|
939
|
-
subscribeTopic: (e) =>
|
|
940
|
-
unsubscribeTopic: (e, t) =>
|
|
941
|
-
listTopicSubscribers: (e) =>
|
|
942
|
-
getQuietHours: (e) =>
|
|
943
|
-
setQuietHours: (e) =>
|
|
944
|
-
clearQuietHours: (e) =>
|
|
945
|
-
enqueueHeld: (e) =>
|
|
946
|
-
pendingDigest: (e) =>
|
|
947
|
-
dueHeld: (e) =>
|
|
948
|
-
clearHeld: (e) =>
|
|
949
|
-
},
|
|
950
|
-
channels:
|
|
951
|
-
store:
|
|
952
|
-
digestWindowMs:
|
|
953
|
-
}),
|
|
1226
|
+
let _ = e.store === void 0, v = { current: e.store ?? Ee() }, y, b = (e) => e.then((e) => (de(y, i), e)), x = {
|
|
1227
|
+
addInbox: (e) => b(v.current.addInbox(e)),
|
|
1228
|
+
listInbox: (e, t) => v.current.listInbox(e, t),
|
|
1229
|
+
markRead: (e, t) => b(v.current.markRead(e, t)),
|
|
1230
|
+
markUnread: (e, t) => b(v.current.markUnread(e, t)),
|
|
1231
|
+
markAllRead: (e) => b(v.current.markAllRead(e)),
|
|
1232
|
+
setArchived: (e, t, n) => b(v.current.setArchived(e, t, n)),
|
|
1233
|
+
unreadCount: (e) => v.current.unreadCount(e),
|
|
1234
|
+
getPreferences: (e) => v.current.getPreferences(e),
|
|
1235
|
+
setPreference: (e) => v.current.setPreference(e),
|
|
1236
|
+
recordDelivery: (e) => v.current.recordDelivery(e),
|
|
1237
|
+
listDeliveries: (e) => v.current.listDeliveries(e),
|
|
1238
|
+
markClicked: (e) => v.current.markClicked(e),
|
|
1239
|
+
subscribeTopic: (e) => v.current.subscribeTopic(e),
|
|
1240
|
+
unsubscribeTopic: (e, t) => v.current.unsubscribeTopic(e, t),
|
|
1241
|
+
listTopicSubscribers: (e) => v.current.listTopicSubscribers(e),
|
|
1242
|
+
getQuietHours: (e) => v.current.getQuietHours(e),
|
|
1243
|
+
setQuietHours: (e) => v.current.setQuietHours(e),
|
|
1244
|
+
clearQuietHours: (e) => v.current.clearQuietHours(e),
|
|
1245
|
+
enqueueHeld: (e) => v.current.enqueueHeld(e),
|
|
1246
|
+
pendingDigest: (e) => v.current.pendingDigest(e),
|
|
1247
|
+
dueHeld: (e) => v.current.dueHeld(e),
|
|
1248
|
+
clearHeld: (e) => v.current.clearHeld(e)
|
|
1249
|
+
}, S = e.channels ?? [ve()], C = S.some((e) => e.id === "inApp") ? S : [...S, Oe(x)], w = e.digestWindowMs ?? 0, T = C.some((e) => e.id === "webPush"), E = je({
|
|
1250
|
+
channels: C,
|
|
1251
|
+
store: x,
|
|
1252
|
+
digestWindowMs: w
|
|
1253
|
+
}), k, A = [
|
|
954
1254
|
{
|
|
955
1255
|
...r,
|
|
956
1256
|
description: "The caller's in-app inbox items.",
|
|
957
|
-
execute: (e, t) => g.promise(async () =>
|
|
1257
|
+
execute: (e, t) => g.promise(async () => E.inbox(await h(t), e))
|
|
958
1258
|
},
|
|
959
1259
|
{
|
|
960
|
-
...
|
|
1260
|
+
...m,
|
|
961
1261
|
description: "The caller's unread count.",
|
|
962
|
-
execute: (e, t) => g.promise(async () => ({ count: await
|
|
1262
|
+
execute: (e, t) => g.promise(async () => ({ count: await E.unreadCount(await h(t)) }))
|
|
963
1263
|
},
|
|
964
1264
|
{
|
|
965
|
-
...
|
|
1265
|
+
...o,
|
|
966
1266
|
description: "Mark an inbox item read.",
|
|
967
|
-
execute: (e, t) => g.promise(async () => ({ ok: await
|
|
1267
|
+
execute: (e, t) => g.promise(async () => ({ ok: await E.markRead(e.id, await h(t)) }))
|
|
968
1268
|
},
|
|
969
1269
|
{
|
|
970
|
-
...
|
|
1270
|
+
...s,
|
|
971
1271
|
description: "Mark an inbox item unread again.",
|
|
972
|
-
execute: (e, t) => g.promise(async () => ({ ok: await
|
|
1272
|
+
execute: (e, t) => g.promise(async () => ({ ok: await E.markUnread(e.id, await h(t)) }))
|
|
973
1273
|
},
|
|
974
1274
|
{
|
|
975
|
-
...
|
|
1275
|
+
...a,
|
|
976
1276
|
description: "Mark every unread item in the caller's inbox read.",
|
|
977
|
-
execute: (e, t) => g.promise(async () => ({ count: await
|
|
1277
|
+
execute: (e, t) => g.promise(async () => ({ count: await E.markAllRead(await h(t)) }))
|
|
978
1278
|
},
|
|
979
1279
|
{
|
|
980
1280
|
...t,
|
|
981
1281
|
description: "Archive an inbox item — a different state from read; this is what empties the inbox.",
|
|
982
|
-
execute: (e, t) => g.promise(async () => ({ ok: await
|
|
1282
|
+
execute: (e, t) => g.promise(async () => ({ ok: await E.setArchived(e.id, await h(t), !0) }))
|
|
983
1283
|
},
|
|
984
1284
|
{
|
|
985
|
-
...
|
|
1285
|
+
...p,
|
|
986
1286
|
description: "Return an archived item to the inbox.",
|
|
987
|
-
execute: (e, t) => g.promise(async () => ({ ok: await
|
|
1287
|
+
execute: (e, t) => g.promise(async () => ({ ok: await E.setArchived(e.id, await h(t), !1) }))
|
|
988
1288
|
},
|
|
989
1289
|
{
|
|
990
|
-
...
|
|
1290
|
+
...l,
|
|
991
1291
|
description: "The caller's channel preferences.",
|
|
992
|
-
execute: (e, t) => g.promise(async () =>
|
|
1292
|
+
execute: (e, t) => g.promise(async () => E.getPreferences(await h(t)))
|
|
993
1293
|
},
|
|
994
1294
|
{
|
|
995
|
-
...
|
|
1295
|
+
...u,
|
|
996
1296
|
description: "Toggle a channel on/off for a category.",
|
|
997
1297
|
execute: (e, t) => g.promise(async () => {
|
|
998
1298
|
let { category: n, channel: r, enabled: i } = e;
|
|
999
|
-
return await
|
|
1299
|
+
return await E.setPreference({
|
|
1000
1300
|
subjectId: await h(t),
|
|
1001
1301
|
category: n,
|
|
1002
1302
|
channel: r,
|
|
@@ -1005,21 +1305,21 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
1005
1305
|
})
|
|
1006
1306
|
},
|
|
1007
1307
|
{
|
|
1008
|
-
...
|
|
1308
|
+
...f,
|
|
1009
1309
|
description: "Subscribe the caller to a broadcast topic.",
|
|
1010
|
-
execute: (e, t) => g.promise(async () => (await
|
|
1310
|
+
execute: (e, t) => g.promise(async () => (await E.subscribe(e.topic, await h(t), Pt(t)), { ok: !0 }))
|
|
1011
1311
|
},
|
|
1012
1312
|
{
|
|
1013
|
-
...
|
|
1313
|
+
...ee,
|
|
1014
1314
|
description: "Unsubscribe the caller from a broadcast topic.",
|
|
1015
|
-
execute: (e, t) => g.promise(async () => (await
|
|
1315
|
+
execute: (e, t) => g.promise(async () => (await E.unsubscribe(e.topic, await h(t)), { ok: !0 }))
|
|
1016
1316
|
},
|
|
1017
1317
|
{
|
|
1018
|
-
...
|
|
1318
|
+
...d,
|
|
1019
1319
|
description: "Set the caller's quiet-hours (DND) window.",
|
|
1020
1320
|
execute: (e, t) => g.promise(async () => {
|
|
1021
1321
|
let n = e;
|
|
1022
|
-
return await
|
|
1322
|
+
return await E.setQuietHours({
|
|
1023
1323
|
subjectId: await h(t),
|
|
1024
1324
|
startMinute: n.startMinute,
|
|
1025
1325
|
endMinute: n.endMinute,
|
|
@@ -1031,59 +1331,59 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
1031
1331
|
{
|
|
1032
1332
|
...n,
|
|
1033
1333
|
description: "Clear the caller's quiet-hours window.",
|
|
1034
|
-
execute: (e, t) => g.promise(async () => (await
|
|
1334
|
+
execute: (e, t) => g.promise(async () => (await E.clearQuietHours(await h(t)), { ok: !0 }))
|
|
1035
1335
|
}
|
|
1036
|
-
],
|
|
1336
|
+
], j = T ? [
|
|
1037
1337
|
{
|
|
1038
|
-
...
|
|
1338
|
+
...te,
|
|
1039
1339
|
description: "The VAPID application-server key a subscribing browser needs.",
|
|
1040
|
-
execute: () => g.sync(() => ({ key:
|
|
1340
|
+
execute: () => g.sync(() => ({ key: nt(q().channelOptions?.privateKey) }))
|
|
1041
1341
|
},
|
|
1042
1342
|
{
|
|
1043
|
-
...
|
|
1343
|
+
...ie,
|
|
1044
1344
|
description: "Register the calling subject's browser push subscription (one row per endpoint).",
|
|
1045
1345
|
execute: (e, t) => g.promise(async () => {
|
|
1046
1346
|
let n = e;
|
|
1047
|
-
return await
|
|
1347
|
+
return await q().store.upsert({
|
|
1048
1348
|
subjectId: await h(t),
|
|
1049
1349
|
endpoint: n.endpoint,
|
|
1050
|
-
endpointHash:
|
|
1350
|
+
endpointHash: W(n.endpoint),
|
|
1051
1351
|
p256dh: n.p256dh,
|
|
1052
1352
|
auth: n.auth,
|
|
1053
1353
|
ua: n.ua ?? null,
|
|
1054
|
-
tenantId:
|
|
1354
|
+
tenantId: Pt(t)
|
|
1055
1355
|
}), { ok: !0 };
|
|
1056
1356
|
})
|
|
1057
1357
|
},
|
|
1058
1358
|
{
|
|
1059
|
-
...
|
|
1359
|
+
...ae,
|
|
1060
1360
|
description: "Remove one of the calling subject's own subscriptions.",
|
|
1061
|
-
execute: (e, t) => g.promise(async () => ({ ok: await
|
|
1361
|
+
execute: (e, t) => g.promise(async () => ({ ok: await q().store.removeForSubject(W(e.endpoint), await h(t)) }))
|
|
1062
1362
|
},
|
|
1063
1363
|
{
|
|
1064
|
-
...
|
|
1364
|
+
...re,
|
|
1065
1365
|
description: "How many push endpoints the calling subject has registered.",
|
|
1066
|
-
execute: (e, t) => g.promise(async () => ({ endpoints: await
|
|
1366
|
+
execute: (e, t) => g.promise(async () => ({ endpoints: await q().store.countForSubject(await h(t)) }))
|
|
1067
1367
|
}
|
|
1068
|
-
] : [],
|
|
1368
|
+
] : [], M = (e) => ({
|
|
1069
1369
|
kind: "json",
|
|
1070
1370
|
data: e
|
|
1071
|
-
}),
|
|
1371
|
+
}), fe = (e, t) => ({
|
|
1072
1372
|
kind: "json",
|
|
1073
1373
|
status: e,
|
|
1074
1374
|
data: { error: t }
|
|
1075
|
-
}),
|
|
1375
|
+
}), N = (e, t) => new URLSearchParams(new URL(e, "http://x").search).get(t), pe = [{
|
|
1076
1376
|
method: "GET",
|
|
1077
1377
|
path: "/deliveries",
|
|
1078
1378
|
scope: "process",
|
|
1079
1379
|
description: "Recent delivery records (newest first) + per-channel + per-status counts.",
|
|
1080
1380
|
handler: () => g.promise(async () => {
|
|
1081
|
-
let e = await
|
|
1381
|
+
let e = await x.listDeliveries({ limit: 200 }), t = {}, n = 0, r = 0;
|
|
1082
1382
|
for (let i of e) t[i.channel] = (t[i.channel] ?? 0) + 1, i.status === "sent" ? n++ : i.status === "failed" && r++;
|
|
1083
1383
|
let i = e.slice(0, 100).map(({ clickToken: e, ...t }) => t);
|
|
1084
|
-
return
|
|
1384
|
+
return M({
|
|
1085
1385
|
deliveries: i,
|
|
1086
|
-
channels:
|
|
1386
|
+
channels: C.map((e) => e.id),
|
|
1087
1387
|
stats: {
|
|
1088
1388
|
sent: n,
|
|
1089
1389
|
failed: r,
|
|
@@ -1097,28 +1397,29 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
1097
1397
|
scope: "process",
|
|
1098
1398
|
description: "In-app inbox for a subject (?subjectId=…).",
|
|
1099
1399
|
handler: (e) => {
|
|
1100
|
-
let t =
|
|
1101
|
-
return t ? g.promise(async () =>
|
|
1102
|
-
items: await
|
|
1103
|
-
unread: await
|
|
1104
|
-
})) : g.succeed(
|
|
1400
|
+
let t = N(e.url, "subjectId");
|
|
1401
|
+
return t ? g.promise(async () => M({
|
|
1402
|
+
items: await x.listInbox(t, { limit: 100 }),
|
|
1403
|
+
unread: await x.unreadCount(t)
|
|
1404
|
+
})) : g.succeed(fe(400, "subjectId query param required"));
|
|
1105
1405
|
}
|
|
1106
1406
|
}];
|
|
1107
|
-
return
|
|
1108
|
-
|
|
1109
|
-
|
|
1407
|
+
return le({
|
|
1408
|
+
privacy: It,
|
|
1409
|
+
name: oe,
|
|
1410
|
+
baseName: Ft,
|
|
1110
1411
|
description: "Unified notifications — email/slack/sms/push/in-app with preference routing, inbox + delivery log.",
|
|
1111
1412
|
permissions: ["inspect:read", "store:write"],
|
|
1112
1413
|
...e.tables === !1 ? {} : { extendSchema: { tables: [
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1414
|
+
Dt,
|
|
1415
|
+
Ot,
|
|
1416
|
+
kt,
|
|
1417
|
+
At,
|
|
1418
|
+
jt,
|
|
1419
|
+
Mt,
|
|
1420
|
+
Ke
|
|
1120
1421
|
] } },
|
|
1121
|
-
...
|
|
1422
|
+
...T ? {
|
|
1122
1423
|
declaredEnv: [{
|
|
1123
1424
|
name: "VOLTRO_VAPID_PRIVATE_KEY",
|
|
1124
1425
|
required: !0,
|
|
@@ -1134,12 +1435,12 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
1134
1435
|
try {
|
|
1135
1436
|
t = JSON.parse(Buffer.from(e.rawBody).toString("utf8")).token;
|
|
1136
1437
|
} catch {}
|
|
1137
|
-
if (typeof t != "string" || t === "" || !
|
|
1438
|
+
if (typeof t != "string" || t === "" || !Ge(t, t)) return {
|
|
1138
1439
|
status: 400,
|
|
1139
1440
|
body: JSON.stringify({ error: "token required" }),
|
|
1140
1441
|
contentType: "application/json"
|
|
1141
1442
|
};
|
|
1142
|
-
let n = await
|
|
1443
|
+
let n = await x.markClicked(t);
|
|
1143
1444
|
return {
|
|
1144
1445
|
status: n ? 200 : 404,
|
|
1145
1446
|
body: JSON.stringify({ ok: n }),
|
|
@@ -1149,27 +1450,27 @@ var F = class extends _.TaggedError()("PushTokenRejected", {
|
|
|
1149
1450
|
maxBodyBytes: 4096
|
|
1150
1451
|
}]
|
|
1151
1452
|
} : {},
|
|
1152
|
-
services:
|
|
1153
|
-
routes: [...
|
|
1154
|
-
rpcClientDescriptors:
|
|
1155
|
-
inspectEndpoints:
|
|
1453
|
+
services: se.succeed($, E),
|
|
1454
|
+
routes: [...A, ...j],
|
|
1455
|
+
rpcClientDescriptors: T ? [...c, ...ne] : c,
|
|
1456
|
+
inspectEndpoints: pe,
|
|
1156
1457
|
bindDataStore: (t) => {
|
|
1157
|
-
|
|
1458
|
+
y = t, _ && (v.current = De(t)), Ye(t);
|
|
1158
1459
|
let n = e.flushIntervalMs ?? 3e4;
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
}, n), typeof
|
|
1460
|
+
k = setInterval(() => {
|
|
1461
|
+
E.flushDue().catch(() => {});
|
|
1462
|
+
}, n), typeof k.unref == "function" && k.unref();
|
|
1162
1463
|
},
|
|
1163
1464
|
onActivate: (e) => g.sync(() => {
|
|
1164
|
-
|
|
1165
|
-
channels:
|
|
1166
|
-
digestWindowMs:
|
|
1465
|
+
T && $e(), e.logger.info("notifications active", {
|
|
1466
|
+
channels: C.map((e) => e.id),
|
|
1467
|
+
digestWindowMs: w
|
|
1167
1468
|
});
|
|
1168
1469
|
}),
|
|
1169
1470
|
onDeactivate: () => g.sync(() => {
|
|
1170
|
-
|
|
1471
|
+
k && clearInterval(k), k = void 0;
|
|
1171
1472
|
})
|
|
1172
1473
|
});
|
|
1173
1474
|
};
|
|
1174
1475
|
//#endregion
|
|
1175
|
-
export {
|
|
1476
|
+
export { lt as APNS_HOSTS, dt as APNS_TOKEN_REJECTIONS, wt as EXPO_PUSH_ENDPOINT, gt as FCM_SCOPE, yt as FCM_TOKEN_REJECTIONS, _t as GOOGLE_TOKEN_URI, X as NOTIFICATION_SEND_EFFECT, $ as NotificationService, I as PushTokenRejected, pt as apnsBody, ft as apnsProviderToken, ht as apnsTransport, je as buildNotificationService, ve as consoleChannel, Se as customChannel, De as dataStoreNotificationStore, qe as dataStorePushSubscriptionStore, Ue as decryptWebPushPayload, h as defineNotification, kt as deliveriesTable, be as emailChannel, He as encryptWebPushPayload, W as endpointHashOf, ot as enqueueNotification, Et as expoPushTransport, xt as fcmAssertion, St as fcmMessage, vt as fcmSendEndpoint, Ct as fcmTransport, Ne as generateVapidPrivateKey, Mt as heldTable, Oe as inAppChannel, ke as inQuietHours, Dt as inboxTable, Tt as isExpoPushToken, Nt as makeSubjectId, Ee as memoryNotificationStore, G as memoryPushSubscriptionStore, R as minuteOfDayInZone, ct as notificationOutboxHandler, Lt as notificationsPlugin, bt as parseServiceAccount, Ot as preferencesTable, we as pushChannel, Ke as pushSubscriptionsTable, Ae as quietHoursEnd, jt as quietHoursTable, Xe as resetWebPushStoreForTest, Te as resolveChannels, Ce as richPushOf, it as sendNotification, We as sendWebPush, xe as smsChannel, At as topicSubscriptionsTable, Le as vapidAuthorization, U as vapidPublicKeyFor, Qe as webPushChannel, nt as webPushPublicKey, ye as webhookChannel };
|