@growth-labs/push 0.4.1 → 0.4.3
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/dist/db/schema.d.ts +19 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +1 -0
- package/dist/db/schema.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +6 -0
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +6 -0
- package/dist/options.js.map +1 -1
- package/dist/routes/subscribe.d.ts.map +1 -1
- package/dist/routes/subscribe.js +3 -1
- package/dist/routes/subscribe.js.map +1 -1
- package/dist/routes/unsubscribe.d.ts.map +1 -1
- package/dist/routes/unsubscribe.js +2 -1
- package/dist/routes/unsubscribe.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/cleanup.d.ts +5 -0
- package/dist/utils/cleanup.d.ts.map +1 -1
- package/dist/utils/cleanup.js +15 -0
- package/dist/utils/cleanup.js.map +1 -1
- package/dist/utils/queue.d.ts +10 -1
- package/dist/utils/queue.d.ts.map +1 -1
- package/dist/utils/queue.js +15 -2
- package/dist/utils/queue.js.map +1 -1
- package/dist/utils/scheduled.d.ts +9 -0
- package/dist/utils/scheduled.d.ts.map +1 -1
- package/dist/utils/scheduled.js +7 -2
- package/dist/utils/scheduled.js.map +1 -1
- package/dist/utils/subscriptions.d.ts +7 -1
- package/dist/utils/subscriptions.d.ts.map +1 -1
- package/dist/utils/subscriptions.js +17 -4
- package/dist/utils/subscriptions.js.map +1 -1
- package/package.json +2 -2
- package/src/db/schema.ts +1 -0
- package/src/index.ts +5 -1
- package/src/options.ts +7 -0
- package/src/routes/subscribe.ts +3 -1
- package/src/routes/unsubscribe.ts +2 -1
- package/src/types.ts +6 -0
- package/src/utils/cleanup.ts +19 -1
- package/src/utils/queue.ts +15 -1
- package/src/utils/scheduled.ts +16 -1
- package/src/utils/subscriptions.ts +21 -4
package/dist/db/schema.d.ts
CHANGED
|
@@ -186,6 +186,25 @@ export declare const pushSubscriptions: import("drizzle-orm/sqlite-core").SQLite
|
|
|
186
186
|
}, {}, {
|
|
187
187
|
length: number | undefined;
|
|
188
188
|
}>;
|
|
189
|
+
siteId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
190
|
+
name: "site_id";
|
|
191
|
+
tableName: "gl_push_subscriptions";
|
|
192
|
+
dataType: "string";
|
|
193
|
+
columnType: "SQLiteText";
|
|
194
|
+
data: string;
|
|
195
|
+
driverParam: string;
|
|
196
|
+
notNull: false;
|
|
197
|
+
hasDefault: false;
|
|
198
|
+
isPrimaryKey: false;
|
|
199
|
+
isAutoincrement: false;
|
|
200
|
+
hasRuntimeDefault: false;
|
|
201
|
+
enumValues: [string, ...string[]];
|
|
202
|
+
baseColumn: never;
|
|
203
|
+
identity: undefined;
|
|
204
|
+
generated: undefined;
|
|
205
|
+
}, {}, {
|
|
206
|
+
length: number | undefined;
|
|
207
|
+
}>;
|
|
189
208
|
};
|
|
190
209
|
dialect: "sqlite";
|
|
191
210
|
}>;
|
package/dist/db/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAY5B,CAAA"}
|
package/dist/db/schema.js
CHANGED
|
@@ -10,5 +10,6 @@ export const pushSubscriptions = sqliteTable('gl_push_subscriptions', {
|
|
|
10
10
|
failureCount: integer('failure_count').notNull().default(0),
|
|
11
11
|
userAgent: text('user_agent'),
|
|
12
12
|
country: text('country'),
|
|
13
|
+
siteId: text('site_id'),
|
|
13
14
|
});
|
|
14
15
|
//# sourceMappingURL=schema.js.map
|
package/dist/db/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAA;AAEpE,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAC,uBAAuB,EAAE;IACrE,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE;IAC7C,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IACvC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE;IACnC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE;IACjE,cAAc,EAAE,OAAO,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAClE,YAAY,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3D,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC;IAC7B,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAA;AAEpE,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAC,uBAAuB,EAAE;IACrE,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE;IAC7C,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IACvC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE;IACnC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE;IACjE,cAAc,EAAE,OAAO,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAClE,YAAY,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3D,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC;IAC7B,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;IACxB,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC;CACvB,CAAC,CAAA"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,cAAc,CAAA;AAYlE,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,gBAAgB,CA0DnE"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { fileURLToPath } from 'node:url';
|
|
2
2
|
import { pushOptionsSchema } from './options.js';
|
|
3
|
-
|
|
3
|
+
function isSourceEntrypoint() {
|
|
4
|
+
return typeof import.meta.url === 'string' && import.meta.url.endsWith('.ts');
|
|
5
|
+
}
|
|
6
|
+
const ENTRYPOINT_EXTENSION = isSourceEntrypoint() ? '.ts' : '.js';
|
|
4
7
|
function resolveEntrypoint(path) {
|
|
5
8
|
return fileURLToPath(new URL(`${path}${ENTRYPOINT_EXTENSION}`, import.meta.url));
|
|
6
9
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAoB,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAElE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAoB,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAElE,SAAS,kBAAkB;IAC1B,OAAO,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AAC9E,CAAC;AAED,MAAM,oBAAoB,GAAG,kBAAkB,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAA;AAEjE,SAAS,iBAAiB,CAAC,IAAY;IACtC,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,oBAAoB,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AACjF,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,OAAoB;IAChD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAEjD,MAAM,aAAa,GAAG;QACrB,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;KACzB,CAAA;IAED,MAAM,eAAe,GAAG,iCAAiC,CAAA;IACzD,MAAM,uBAAuB,GAAG,KAAK,eAAe,EAAE,CAAA;IAEtD,OAAO;QACN,IAAI,EAAE,mBAAmB;QACzB,KAAK,EAAE;YACN,oBAAoB,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,EAAE;gBACtE,YAAY,CAAC;oBACZ,IAAI,EAAE;wBACL,OAAO,EAAE;4BACR;gCACC,IAAI,EAAE,yBAAyB;gCAC/B,SAAS,CAAC,EAAE;oCACX,IAAI,EAAE,KAAK,eAAe;wCAAE,OAAO,uBAAuB,CAAA;gCAC3D,CAAC;gCACD,IAAI,CAAC,EAAE;oCACN,IAAI,EAAE,KAAK,uBAAuB,EAAE,CAAC;wCACpC,OAAO,kBAAkB,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,EAAE,CAAA;oCACzD,CAAC;gCACF,CAAC;6BACD;yBACD;qBACD;iBACD,CAAC,CAAA;gBAEF,aAAa,CAAC;oBACb,UAAU,EAAE,iBAAiB,CAAC,mBAAmB,CAAC;oBAClD,KAAK,EAAE,KAAK;iBACZ,CAAC,CAAA;gBAEF,WAAW,CAAC;oBACX,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS;oBAClC,UAAU,EAAE,iBAAiB,CAAC,oBAAoB,CAAC;oBACnD,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAA;gBAEF,WAAW,CAAC;oBACX,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW;oBACpC,UAAU,EAAE,iBAAiB,CAAC,sBAAsB,CAAC;oBACrD,SAAS,EAAE,KAAK;iBAChB,CAAC,CAAA;YACH,CAAC;SACD;KACD,CAAA;AACF,CAAC"}
|
package/dist/options.d.ts
CHANGED
|
@@ -3,7 +3,9 @@ export declare const pushOptionsSchema: z.ZodObject<{
|
|
|
3
3
|
vapidPublicKey: z.ZodString;
|
|
4
4
|
vapidPrivateKey: z.ZodString;
|
|
5
5
|
vapidSubject: z.ZodString;
|
|
6
|
+
siteId: z.ZodString;
|
|
6
7
|
d1Binding: z.ZodDefault<z.ZodString>;
|
|
8
|
+
subscriptionsD1Binding: z.ZodOptional<z.ZodString>;
|
|
7
9
|
queueBinding: z.ZodDefault<z.ZodString>;
|
|
8
10
|
defaults: z.ZodDefault<z.ZodObject<{
|
|
9
11
|
icon: z.ZodOptional<z.ZodString>;
|
|
@@ -56,6 +58,7 @@ export declare const pushOptionsSchema: z.ZodObject<{
|
|
|
56
58
|
vapidPublicKey: string;
|
|
57
59
|
vapidPrivateKey: string;
|
|
58
60
|
vapidSubject: string;
|
|
61
|
+
siteId: string;
|
|
59
62
|
d1Binding: string;
|
|
60
63
|
queueBinding: string;
|
|
61
64
|
defaults: {
|
|
@@ -77,14 +80,17 @@ export declare const pushOptionsSchema: z.ZodObject<{
|
|
|
77
80
|
cleanup: {
|
|
78
81
|
staleAfterDays: number;
|
|
79
82
|
};
|
|
83
|
+
subscriptionsD1Binding?: string | undefined;
|
|
80
84
|
}, {
|
|
81
85
|
vapidPublicKey: string;
|
|
82
86
|
vapidPrivateKey: string;
|
|
83
87
|
vapidSubject: string;
|
|
88
|
+
siteId: string;
|
|
84
89
|
turnstile: {
|
|
85
90
|
secretKey: string;
|
|
86
91
|
};
|
|
87
92
|
d1Binding?: string | undefined;
|
|
93
|
+
subscriptionsD1Binding?: string | undefined;
|
|
88
94
|
queueBinding?: string | undefined;
|
|
89
95
|
defaults?: {
|
|
90
96
|
icon?: string | undefined;
|
package/dist/options.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8C5B,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAC3D,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAA"}
|
package/dist/options.js
CHANGED
|
@@ -3,7 +3,13 @@ export const pushOptionsSchema = z.object({
|
|
|
3
3
|
vapidPublicKey: z.string().min(1),
|
|
4
4
|
vapidPrivateKey: z.string().min(1),
|
|
5
5
|
vapidSubject: z.string().min(1),
|
|
6
|
+
siteId: z.string().min(1),
|
|
6
7
|
d1Binding: z.string().default('SITE_DB'),
|
|
8
|
+
// D1 binding holding gl_push_subscriptions (subscribe/unsubscribe + producer
|
|
9
|
+
// reads). Defaults to d1Binding. Set to the realm push DB (e.g. 'MAILER_DB')
|
|
10
|
+
// on sites whose queue is drained by the central push-platform Worker, so the
|
|
11
|
+
// consumer's status writeback and the site's subscribe/produce share one DB.
|
|
12
|
+
subscriptionsD1Binding: z.string().optional(),
|
|
7
13
|
queueBinding: z.string().default('PUSH_QUEUE'),
|
|
8
14
|
defaults: z
|
|
9
15
|
.object({
|
package/dist/options.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAE/B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;IACxC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC;IAE9C,QAAQ,EAAE,CAAC;SACT,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;QAC/C,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;KACxE,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;IAEb,KAAK,EAAE,CAAC;SACN,MAAM,CAAC;QACP,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;QACnD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;KACrD,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;IAEb,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC;QACnB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;KAC5B,CAAC;IAEF,MAAM,EAAE,CAAC;SACP,MAAM,CAAC;QACP,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC;QACpD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,uBAAuB,CAAC;KACxD,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;IAEb,OAAO,EAAE,CAAC;SACR,MAAM,CAAC;QACP,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;KACvD,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;CACb,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAE/B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;IACxC,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,6EAA6E;IAC7E,sBAAsB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC;IAE9C,QAAQ,EAAE,CAAC;SACT,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;QAC/C,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;KACxE,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;IAEb,KAAK,EAAE,CAAC;SACN,MAAM,CAAC;QACP,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;QACnD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;KACrD,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;IAEb,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC;QACnB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;KAC5B,CAAC;IAEF,MAAM,EAAE,CAAC;SACP,MAAM,CAAC;QACP,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC;QACpD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,uBAAuB,CAAC;KACxD,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;IAEb,OAAO,EAAE,CAAC;SACR,MAAM,CAAC;QACP,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;KACvD,CAAC;SACD,OAAO,CAAC,EAAE,CAAC;CACb,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["../../src/routes/subscribe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAKrC,eAAO,MAAM,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["../../src/routes/subscribe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAKrC,eAAO,MAAM,IAAI,EAAE,QA4ElB,CAAA"}
|
package/dist/routes/subscribe.js
CHANGED
|
@@ -30,7 +30,8 @@ export const POST = async ({ request }) => {
|
|
|
30
30
|
headers: { 'Content-Type': 'application/json' },
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
-
const
|
|
33
|
+
const subsBinding = config.subscriptionsD1Binding ?? config.d1Binding;
|
|
34
|
+
const db = envMap[subsBinding];
|
|
34
35
|
const existing = await findSubscriptionByEndpoint(db, endpoint);
|
|
35
36
|
if (existing) {
|
|
36
37
|
await saveSubscription(db, {
|
|
@@ -56,6 +57,7 @@ export const POST = async ({ request }) => {
|
|
|
56
57
|
failureCount: 0,
|
|
57
58
|
userAgent: request.headers.get('User-Agent'),
|
|
58
59
|
country: request.headers.get('CF-IPCountry') ?? null,
|
|
60
|
+
siteId: config.siteId,
|
|
59
61
|
});
|
|
60
62
|
trackPushSubscription('subscribed', {
|
|
61
63
|
country: request.headers.get('CF-IPCountry') ?? undefined,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscribe.js","sourceRoot":"","sources":["../../src/routes/subscribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,MAAM,MAAM,iCAAiC,CAAA;AAEpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEvD,MAAM,CAAC,MAAM,IAAI,GAAa,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IACnD,MAAM,IAAI,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAA4B,CAAA;IAE9D,MAAM,cAAc,GAAG,IAAI,CAAC,cAAoC,CAAA;IAChE,IAAI,CAAC,cAAc,EAAE,CAAC;QACrB,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,EAAE;YACzE,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAC/C,CAAC,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,GAA8B,CAAA;IAC7C,MAAM,eAAe,GACnB,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAY,IAAK,MAAM,CAAC,gBAA2B,CAAA;IACtF,MAAM,cAAc,GAAG,MAAM,eAAe,CAC3C,cAAc,EACd,eAAe,EACf,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CACvC,CAAA;IACD,IAAI,CAAC,cAAc,EAAE,CAAC;QACrB,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE;YAC9D,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAC/C,CAAC,CAAA;IACH,CAAC;IAED,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAA4B,CAAA;IAChE,MAAM,QAAQ,GAAG,GAAG,CAAC,QAA8B,CAAA;IACnD,MAAM,IAAI,GAAG,GAAG,CAAC,IAA0C,CAAA;IAC3D,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;QAC/C,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,EAAE;YACtE,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAC/C,CAAC,CAAA;IACH,CAAC;IAED,MAAM,
|
|
1
|
+
{"version":3,"file":"subscribe.js","sourceRoot":"","sources":["../../src/routes/subscribe.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unsubscribe.d.ts","sourceRoot":"","sources":["../../src/routes/unsubscribe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAIrC,eAAO,MAAM,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"unsubscribe.d.ts","sourceRoot":"","sources":["../../src/routes/unsubscribe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAIrC,eAAO,MAAM,IAAI,EAAE,QAuBlB,CAAA"}
|
|
@@ -11,7 +11,8 @@ export const POST = async ({ request }) => {
|
|
|
11
11
|
headers: { 'Content-Type': 'application/json' },
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
|
-
const
|
|
14
|
+
const subsBinding = config.subscriptionsD1Binding ?? config.d1Binding;
|
|
15
|
+
const db = env[subsBinding];
|
|
15
16
|
const deleted = await deleteSubscriptionByEndpoint(db, endpoint);
|
|
16
17
|
if (deleted) {
|
|
17
18
|
trackPushSubscription('unsubscribed').catch(() => { });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unsubscribe.js","sourceRoot":"","sources":["../../src/routes/unsubscribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,MAAM,MAAM,iCAAiC,CAAA;AAEpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAA;AAExE,MAAM,CAAC,MAAM,IAAI,GAAa,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IACnD,MAAM,IAAI,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAA4B,CAAA;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAA8B,CAAA;IAEpD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,EAAE;YAClE,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAC/C,CAAC,CAAA;IACH,CAAC;IAED,MAAM,EAAE,GAAI,GAA+B,CAAC,
|
|
1
|
+
{"version":3,"file":"unsubscribe.js","sourceRoot":"","sources":["../../src/routes/unsubscribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,MAAM,MAAM,iCAAiC,CAAA;AAEpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAA;AAExE,MAAM,CAAC,MAAM,IAAI,GAAa,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IACnD,MAAM,IAAI,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAA4B,CAAA;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAA8B,CAAA;IAEpD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,EAAE;YAClE,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAC/C,CAAC,CAAA;IACH,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,sBAAsB,IAAI,MAAM,CAAC,SAAS,CAAA;IACrE,MAAM,EAAE,GAAI,GAA+B,CAAC,WAAW,CAAe,CAAA;IACtE,MAAM,OAAO,GAAG,MAAM,4BAA4B,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAA;IAEhE,IAAI,OAAO,EAAE,CAAC;QACb,qBAAqB,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IACtD,CAAC;IAED,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE;QAC1D,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;KAC/C,CAAC,CAAA;AACH,CAAC,CAAA"}
|
package/dist/types.d.ts
CHANGED
|
@@ -28,6 +28,9 @@ export interface PushSubscriptionRecord {
|
|
|
28
28
|
failureCount: number;
|
|
29
29
|
userAgent: string | null;
|
|
30
30
|
country: string | null;
|
|
31
|
+
/** Site identifier. Null in single-tenant mode; set to options.siteId in
|
|
32
|
+
* central-consumer mode so a shared subscriptions table can be scoped per site. */
|
|
33
|
+
siteId?: string | null;
|
|
31
34
|
}
|
|
32
35
|
/**
|
|
33
36
|
* Queue message shape for batch sends.
|
|
@@ -93,7 +96,10 @@ export interface PushLocals {
|
|
|
93
96
|
/** Serializable config exported by the virtual module */
|
|
94
97
|
export interface PushConfig {
|
|
95
98
|
vapidPublicKey: string;
|
|
99
|
+
siteId: string;
|
|
96
100
|
d1Binding: string;
|
|
101
|
+
/** Optional separate DB holding gl_push_subscriptions. Defaults to d1Binding. */
|
|
102
|
+
subscriptionsD1Binding?: string;
|
|
97
103
|
queueBinding: string;
|
|
98
104
|
defaults: {
|
|
99
105
|
icon?: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/cleanup.d.ts
CHANGED
|
@@ -4,9 +4,14 @@ interface CleanupResult {
|
|
|
4
4
|
/**
|
|
5
5
|
* Remove stale or high-failure subscriptions.
|
|
6
6
|
* Call from a cron trigger or maintenance script.
|
|
7
|
+
*
|
|
8
|
+
* When `siteId` is provided the delete is scoped to that site's rows only —
|
|
9
|
+
* required in central-consumer mode where the table is shared across sites.
|
|
10
|
+
* Omit `siteId` for the back-compat single-tenant path (no filter applied).
|
|
7
11
|
*/
|
|
8
12
|
export declare function cleanupStaleSubscriptions(db: D1Database, options: {
|
|
9
13
|
staleAfterDays: number;
|
|
14
|
+
siteId?: string;
|
|
10
15
|
}): Promise<CleanupResult>;
|
|
11
16
|
export {};
|
|
12
17
|
//# sourceMappingURL=cleanup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/utils/cleanup.ts"],"names":[],"mappings":"AAAA,UAAU,aAAa;IACtB,OAAO,EAAE,MAAM,CAAA;CACf;AAED
|
|
1
|
+
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/utils/cleanup.ts"],"names":[],"mappings":"AAAA,UAAU,aAAa;IACtB,OAAO,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC9C,EAAE,EAAE,UAAU,EACd,OAAO,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAClD,OAAO,CAAC,aAAa,CAAC,CA8BxB"}
|
package/dist/utils/cleanup.js
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Remove stale or high-failure subscriptions.
|
|
3
3
|
* Call from a cron trigger or maintenance script.
|
|
4
|
+
*
|
|
5
|
+
* When `siteId` is provided the delete is scoped to that site's rows only —
|
|
6
|
+
* required in central-consumer mode where the table is shared across sites.
|
|
7
|
+
* Omit `siteId` for the back-compat single-tenant path (no filter applied).
|
|
4
8
|
*/
|
|
5
9
|
export async function cleanupStaleSubscriptions(db, options) {
|
|
6
10
|
const cutoff = Math.floor(Date.now() / 1000) - options.staleAfterDays * 86400;
|
|
11
|
+
if (options.siteId !== undefined) {
|
|
12
|
+
const result = await db
|
|
13
|
+
.prepare(`DELETE FROM gl_push_subscriptions
|
|
14
|
+
WHERE site_id = ?
|
|
15
|
+
AND ((last_delivery_at IS NOT NULL AND last_delivery_at < ?)
|
|
16
|
+
OR (last_delivery_at IS NULL AND created_at < ?)
|
|
17
|
+
OR failure_count >= 10)`)
|
|
18
|
+
.bind(options.siteId, cutoff, cutoff)
|
|
19
|
+
.run();
|
|
20
|
+
return { removed: result.meta?.changes ?? 0 };
|
|
21
|
+
}
|
|
7
22
|
const result = await db
|
|
8
23
|
.prepare(`DELETE FROM gl_push_subscriptions
|
|
9
24
|
WHERE (last_delivery_at IS NOT NULL AND last_delivery_at < ?)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/utils/cleanup.ts"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/utils/cleanup.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC9C,EAAc,EACd,OAAoD;IAEpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,cAAc,GAAG,KAAK,CAAA;IAE7E,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,EAAE;aACrB,OAAO,CACP;;;;iCAI6B,CAC7B;aACA,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;aACpC,GAAG,EAAE,CAAA;QACP,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,EAAE,CAAA;IAC9C,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,EAAE;SACrB,OAAO,CACP;;;4BAGyB,CACzB;SACA,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;SACpB,GAAG,EAAE,CAAA;IAEP,OAAO;QACN,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC;KAClC,CAAA;AACF,CAAC"}
|
package/dist/utils/queue.d.ts
CHANGED
|
@@ -16,8 +16,17 @@ export declare function enqueuePushBatch(subscriptions: PushSubscriptionRecord[]
|
|
|
16
16
|
/**
|
|
17
17
|
* Page through all subscriptions in D1 and enqueue batches as it goes.
|
|
18
18
|
* Never holds more than one page of subscriptions in memory.
|
|
19
|
+
*
|
|
20
|
+
* Central-consumer mode: pass `centralOptions.subscriptionsDb` to read from a
|
|
21
|
+
* shared realm D1 instead of `db`. A `site_id` filter is automatically applied
|
|
22
|
+
* using `context.siteId` so the site only fans out its own subscriptions.
|
|
23
|
+
*
|
|
24
|
+
* Back-compat (single-tenant): omit `centralOptions` — reads from `db` with no
|
|
25
|
+
* site_id filter, preserving existing behavior.
|
|
19
26
|
*/
|
|
20
|
-
export declare function enqueuePushBatchAll(db: D1Database, payload: PushPayload, context: EnqueueContext, options?: PushSendOptions
|
|
27
|
+
export declare function enqueuePushBatchAll(db: D1Database, payload: PushPayload, context: EnqueueContext, options?: PushSendOptions, centralOptions?: {
|
|
28
|
+
subscriptionsDb: D1Database;
|
|
29
|
+
}): Promise<{
|
|
21
30
|
batches: number;
|
|
22
31
|
totalSubscriptions: number;
|
|
23
32
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/utils/queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EAEtB,oBAAoB,EACpB,MAAM,aAAa,CAAA;AASpB,UAAU,cAAc;IACvB,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACrC,aAAa,EAAE,sBAAsB,EAAE,EACvC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,eAAoB,GAC3B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,CAAC,CAoB1D;AAED
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/utils/queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EAEtB,oBAAoB,EACpB,MAAM,aAAa,CAAA;AASpB,UAAU,cAAc;IACvB,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACrC,aAAa,EAAE,sBAAsB,EAAE,EACvC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,eAAoB,GAC3B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,CAAC,CAoB1D;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACxC,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,eAAoB,EAC7B,cAAc,CAAC,EAAE;IAAE,eAAe,EAAE,UAAU,CAAA;CAAE,GAC9C,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,CAAC,CA8B1D;AAED,UAAU,oBAAoB;IAC7B,EAAE,EAAE,UAAU,CAAA;IACd,KAAK,EAAE;QACN,OAAO,EAAE,MAAM,CAAA;QACf,SAAS,EAAE,MAAM,CAAA;QACjB,UAAU,EAAE,MAAM,CAAA;KAClB,CAAA;IACD,QAAQ,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,GAAG,EAAE,MAAM,CAAA;QACX,OAAO,EAAE,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;KAC/C,CAAA;IACD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,oBAAoB,CAAA;IACvC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B;AAqBD;;;;;GAKG;AACH,wBAAsB,eAAe,CACpC,KAAK,EAAE,YAAY,CAAC,gBAAgB,CAAC,EACrC,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
|
package/dist/utils/queue.js
CHANGED
|
@@ -27,14 +27,27 @@ export async function enqueuePushBatch(subscriptions, payload, context, options
|
|
|
27
27
|
/**
|
|
28
28
|
* Page through all subscriptions in D1 and enqueue batches as it goes.
|
|
29
29
|
* Never holds more than one page of subscriptions in memory.
|
|
30
|
+
*
|
|
31
|
+
* Central-consumer mode: pass `centralOptions.subscriptionsDb` to read from a
|
|
32
|
+
* shared realm D1 instead of `db`. A `site_id` filter is automatically applied
|
|
33
|
+
* using `context.siteId` so the site only fans out its own subscriptions.
|
|
34
|
+
*
|
|
35
|
+
* Back-compat (single-tenant): omit `centralOptions` — reads from `db` with no
|
|
36
|
+
* site_id filter, preserving existing behavior.
|
|
30
37
|
*/
|
|
31
|
-
export async function enqueuePushBatchAll(db, payload, context, options = {}) {
|
|
38
|
+
export async function enqueuePushBatchAll(db, payload, context, options = {}, centralOptions) {
|
|
32
39
|
const batchSize = context.batchSize ?? 100;
|
|
40
|
+
const subsDb = centralOptions?.subscriptionsDb ?? db;
|
|
41
|
+
const siteIdFilter = centralOptions?.subscriptionsDb !== undefined ? context.siteId : undefined;
|
|
33
42
|
let offset = 0;
|
|
34
43
|
let batches = 0;
|
|
35
44
|
let total = 0;
|
|
36
45
|
while (true) {
|
|
37
|
-
const subs = await getAllSubscriptions(
|
|
46
|
+
const subs = await getAllSubscriptions(subsDb, {
|
|
47
|
+
limit: batchSize,
|
|
48
|
+
offset,
|
|
49
|
+
siteId: siteIdFilter,
|
|
50
|
+
});
|
|
38
51
|
if (subs.length === 0)
|
|
39
52
|
break;
|
|
40
53
|
const message = {
|
package/dist/utils/queue.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/utils/queue.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoB,aAAa,EAAE,MAAM,WAAW,CAAA;AAC3D,OAAO,EACN,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,GACb,MAAM,oBAAoB,CAAA;AAQ3B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACrC,aAAuC,EACvC,OAAoB,EACpB,OAAuB,EACvB,UAA2B,EAAE;IAE7B,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,GAAG,CAAA;IAC1C,IAAI,OAAO,GAAG,CAAC,CAAA;IAEf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1D,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,CAAA;QACnD,MAAM,OAAO,GAAqB;YACjC,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,aAAa,EAAE,KAAK;YACpB,OAAO;YACP,OAAO;SACP,CAAA;QACD,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACjC,OAAO,EAAE,CAAA;IACV,CAAC;IAED,OAAO;QACN,OAAO;QACP,kBAAkB,EAAE,aAAa,CAAC,MAAM;KACxC,CAAA;AACF,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/utils/queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,11 +2,20 @@ import type { PushPayload, PushSendOptions } from '../types.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Context required by the scheduled push handler.
|
|
4
4
|
* The consumer provides this from their cron trigger's env bindings.
|
|
5
|
+
*
|
|
6
|
+
* Central-consumer mode: set `subscriptionsDb` to the shared realm D1 holding
|
|
7
|
+
* gl_push_subscriptions. The producer will read from that DB and filter by
|
|
8
|
+
* `siteId`, and cleanup will scope deletes to this site's rows only.
|
|
9
|
+
*
|
|
10
|
+
* Back-compat (single-tenant): omit `subscriptionsDb` — `db` is used for all
|
|
11
|
+
* operations and no site_id filter is applied.
|
|
5
12
|
*/
|
|
6
13
|
export interface ScheduledPushContext {
|
|
7
14
|
db: D1Database;
|
|
8
15
|
queue: Queue;
|
|
9
16
|
siteId: string;
|
|
17
|
+
/** Optional separate DB holding gl_push_subscriptions (central mode). */
|
|
18
|
+
subscriptionsDb?: D1Database;
|
|
10
19
|
batchSize?: number;
|
|
11
20
|
cleanup: {
|
|
12
21
|
staleAfterDays: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduled.d.ts","sourceRoot":"","sources":["../../src/utils/scheduled.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAK/D
|
|
1
|
+
{"version":3,"file":"scheduled.d.ts","sourceRoot":"","sources":["../../src/utils/scheduled.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAK/D;;;;;;;;;;GAUG;AACH,MAAM,WAAW,oBAAoB;IACpC,EAAE,EAAE,UAAU,CAAA;IACd,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,yEAAyE;IACzE,eAAe,CAAC,EAAE,UAAU,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE;QACR,cAAc,EAAE,MAAM,CAAA;KACtB,CAAA;CACD;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE;QACb,OAAO,EAAE,MAAM,CAAA;QACf,kBAAkB,EAAE,MAAM,CAAA;KAC1B,GAAG,IAAI,CAAA;IACR,OAAO,EAAE;QACR,OAAO,EAAE,MAAM,CAAA;KACf,CAAA;CACD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,EAAE;IAChD,EAAE,EAAE,UAAU,CAAA;CACd,KAAK,OAAO,CAAC;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,GAAG,IAAI,CAAC,CAAA;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,oBAAoB,EAC7B,eAAe,EAAE,wBAAwB,GACvC,OAAO,CAAC,mBAAmB,CAAC,CA8C9B"}
|
package/dist/utils/scheduled.js
CHANGED
|
@@ -48,7 +48,10 @@ export async function handleScheduledPush(context, contentProvider) {
|
|
|
48
48
|
const content = await contentProvider({ db: context.db });
|
|
49
49
|
// Step 2: Enqueue notifications if content is available
|
|
50
50
|
if (content) {
|
|
51
|
-
const
|
|
51
|
+
const centralOptions = context.subscriptionsDb
|
|
52
|
+
? { subscriptionsDb: context.subscriptionsDb }
|
|
53
|
+
: undefined;
|
|
54
|
+
const enqueueResult = await enqueuePushBatchAll(context.db, content.payload, { queue: context.queue, siteId: context.siteId, batchSize: context.batchSize }, content.options ?? {}, centralOptions);
|
|
52
55
|
notification = {
|
|
53
56
|
batches: enqueueResult.batches,
|
|
54
57
|
totalSubscriptions: enqueueResult.totalSubscriptions,
|
|
@@ -62,8 +65,10 @@ export async function handleScheduledPush(context, contentProvider) {
|
|
|
62
65
|
});
|
|
63
66
|
}
|
|
64
67
|
// Step 3: Always clean up stale subscriptions
|
|
65
|
-
const
|
|
68
|
+
const cleanupDb = context.subscriptionsDb ?? context.db;
|
|
69
|
+
const cleanupResult = await cleanupStaleSubscriptions(cleanupDb, {
|
|
66
70
|
staleAfterDays: context.cleanup.staleAfterDays,
|
|
71
|
+
siteId: context.subscriptionsDb !== undefined ? context.siteId : undefined,
|
|
67
72
|
});
|
|
68
73
|
return {
|
|
69
74
|
notification,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduled.js","sourceRoot":"","sources":["../../src/utils/scheduled.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"scheduled.js","sourceRoot":"","sources":["../../src/utils/scheduled.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAyEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,OAA6B,EAC7B,eAAyC;IAEzC,IAAI,YAAY,GAAwC,IAAI,CAAA;IAE5D,2CAA2C;IAC3C,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;IAEzD,wDAAwD;IACxD,IAAI,OAAO,EAAE,CAAC;QACb,MAAM,cAAc,GAAG,OAAO,CAAC,eAAe;YAC7C,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE;YAC9C,CAAC,CAAC,SAAS,CAAA;QACZ,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAC9C,OAAO,CAAC,EAAE,EACV,OAAO,CAAC,OAAO,EACf,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,EAC9E,OAAO,CAAC,OAAO,IAAI,EAAE,EACrB,cAAc,CACd,CAAA;QAED,YAAY,GAAG;YACd,OAAO,EAAE,aAAa,CAAC,OAAO;YAC9B,kBAAkB,EAAE,aAAa,CAAC,kBAAkB;SACpD,CAAA;QAED,0EAA0E;QAC1E,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE;YACpC,IAAI,EAAE,aAAa,CAAC,kBAAkB;YACtC,MAAM,EAAE,CAAC;YACT,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,EAAE;SACX,CAAC,CAAA;IACH,CAAC;IAED,8CAA8C;IAC9C,MAAM,SAAS,GAAG,OAAO,CAAC,eAAe,IAAI,OAAO,CAAC,EAAE,CAAA;IACvD,MAAM,aAAa,GAAG,MAAM,yBAAyB,CAAC,SAAS,EAAE;QAChE,cAAc,EAAE,OAAO,CAAC,OAAO,CAAC,cAAc;QAC9C,MAAM,EAAE,OAAO,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;KAC1E,CAAC,CAAA;IAEF,OAAO;QACN,YAAY;QACZ,OAAO,EAAE;YACR,OAAO,EAAE,aAAa,CAAC,OAAO;SAC9B;KACD,CAAA;AACF,CAAC"}
|
|
@@ -7,10 +7,16 @@ export declare function findSubscriptionByEndpoint(db: D1Database, endpoint: str
|
|
|
7
7
|
export declare function deleteSubscriptionByEndpoint(db: D1Database, endpoint: string): Promise<boolean>;
|
|
8
8
|
/** Delete a subscription by ID */
|
|
9
9
|
export declare function deleteSubscriptionById(db: D1Database, id: string): Promise<void>;
|
|
10
|
-
/** Get subscriptions with offset-based pagination. Used by enqueuePushBatchAll.
|
|
10
|
+
/** Get subscriptions with offset-based pagination. Used by enqueuePushBatchAll.
|
|
11
|
+
*
|
|
12
|
+
* When `siteId` is provided the query adds `WHERE site_id = ?` so a site only
|
|
13
|
+
* reads its own rows from a shared (central) subscriptions table. Omit siteId
|
|
14
|
+
* for the back-compat single-tenant path (no filter applied).
|
|
15
|
+
*/
|
|
11
16
|
export declare function getAllSubscriptions(db: D1Database, options?: {
|
|
12
17
|
limit?: number;
|
|
13
18
|
offset?: number;
|
|
19
|
+
siteId?: string;
|
|
14
20
|
}): Promise<PushSubscriptionRecord[]>;
|
|
15
21
|
/** Count total subscriptions */
|
|
16
22
|
export declare function countSubscriptions(db: D1Database): Promise<number>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../../src/utils/subscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAEzD,sCAAsC;AACtC,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../../src/utils/subscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAEzD,sCAAsC;AACtC,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BjG;AAED,8CAA8C;AAC9C,wBAAsB,0BAA0B,CAC/C,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAOxC;AAED,4EAA4E;AAC5E,wBAAsB,4BAA4B,CACjD,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,kCAAkC;AAClC,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtF;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACxC,EAAE,EAAE,UAAU,EACd,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAChE,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAoBnC;AAED,gCAAgC;AAChC,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAMxE;AAED,oDAAoD;AACpD,wBAAsB,aAAa,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS7E;AAED,iDAAiD;AACjD,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASrF"}
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
export async function saveSubscription(db, sub) {
|
|
3
3
|
await db
|
|
4
4
|
.prepare(`INSERT INTO gl_push_subscriptions
|
|
5
|
-
(id, endpoint, key_p256dh, key_auth, topics, created_at, last_delivery_at, failure_count, user_agent, country)
|
|
6
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5
|
+
(id, endpoint, key_p256dh, key_auth, topics, created_at, last_delivery_at, failure_count, user_agent, country, site_id)
|
|
6
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
7
7
|
ON CONFLICT (endpoint) DO UPDATE SET
|
|
8
8
|
key_p256dh = excluded.key_p256dh,
|
|
9
9
|
key_auth = excluded.key_auth,
|
|
10
10
|
failure_count = excluded.failure_count,
|
|
11
11
|
last_delivery_at = excluded.last_delivery_at`)
|
|
12
|
-
.bind(sub.id, sub.endpoint, sub.keyP256dh, sub.keyAuth, sub.topics, sub.createdAt, sub.lastDeliveryAt, sub.failureCount, sub.userAgent, sub.country)
|
|
12
|
+
.bind(sub.id, sub.endpoint, sub.keyP256dh, sub.keyAuth, sub.topics, sub.createdAt, sub.lastDeliveryAt, sub.failureCount, sub.userAgent, sub.country, sub.siteId ?? null)
|
|
13
13
|
.run();
|
|
14
14
|
}
|
|
15
15
|
/** Find a subscription by its endpoint URL */
|
|
@@ -32,10 +32,22 @@ export async function deleteSubscriptionByEndpoint(db, endpoint) {
|
|
|
32
32
|
export async function deleteSubscriptionById(db, id) {
|
|
33
33
|
await db.prepare('DELETE FROM gl_push_subscriptions WHERE id = ?').bind(id).run();
|
|
34
34
|
}
|
|
35
|
-
/** Get subscriptions with offset-based pagination. Used by enqueuePushBatchAll.
|
|
35
|
+
/** Get subscriptions with offset-based pagination. Used by enqueuePushBatchAll.
|
|
36
|
+
*
|
|
37
|
+
* When `siteId` is provided the query adds `WHERE site_id = ?` so a site only
|
|
38
|
+
* reads its own rows from a shared (central) subscriptions table. Omit siteId
|
|
39
|
+
* for the back-compat single-tenant path (no filter applied).
|
|
40
|
+
*/
|
|
36
41
|
export async function getAllSubscriptions(db, options = {}) {
|
|
37
42
|
const limit = options.limit ?? 1000;
|
|
38
43
|
const offset = options.offset ?? 0;
|
|
44
|
+
if (options.siteId !== undefined) {
|
|
45
|
+
const result = await db
|
|
46
|
+
.prepare('SELECT * FROM gl_push_subscriptions WHERE site_id = ? ORDER BY created_at ASC LIMIT ? OFFSET ?')
|
|
47
|
+
.bind(options.siteId, limit, offset)
|
|
48
|
+
.all();
|
|
49
|
+
return (result.results ?? []).map(mapRow);
|
|
50
|
+
}
|
|
39
51
|
const result = await db
|
|
40
52
|
.prepare('SELECT * FROM gl_push_subscriptions ORDER BY created_at ASC LIMIT ? OFFSET ?')
|
|
41
53
|
.bind(limit, offset)
|
|
@@ -79,6 +91,7 @@ function mapRow(row) {
|
|
|
79
91
|
failureCount: row.failure_count,
|
|
80
92
|
userAgent: row.user_agent,
|
|
81
93
|
country: row.country,
|
|
94
|
+
siteId: row.site_id ?? null,
|
|
82
95
|
};
|
|
83
96
|
}
|
|
84
97
|
//# sourceMappingURL=subscriptions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../../src/utils/subscriptions.ts"],"names":[],"mappings":"AAEA,sCAAsC;AACtC,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAc,EAAE,GAA2B;IACjF,MAAM,EAAE;SACN,OAAO,CACP;;;;;;;kDAO+C,CAC/C;SACA,IAAI,CACJ,GAAG,CAAC,EAAE,EACN,GAAG,CAAC,QAAQ,EACZ,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,OAAO,EACX,GAAG,CAAC,MAAM,EACV,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,cAAc,EAClB,GAAG,CAAC,YAAY,EAChB,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../../src/utils/subscriptions.ts"],"names":[],"mappings":"AAEA,sCAAsC;AACtC,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAc,EAAE,GAA2B;IACjF,MAAM,EAAE;SACN,OAAO,CACP;;;;;;;kDAO+C,CAC/C;SACA,IAAI,CACJ,GAAG,CAAC,EAAE,EACN,GAAG,CAAC,QAAQ,EACZ,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,OAAO,EACX,GAAG,CAAC,MAAM,EACV,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,cAAc,EAClB,GAAG,CAAC,YAAY,EAChB,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,OAAO,EACX,GAAG,CAAC,MAAM,IAAI,IAAI,CAClB;SACA,GAAG,EAAE,CAAA;AACR,CAAC;AAED,8CAA8C;AAC9C,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC/C,EAAc,EACd,QAAgB;IAEhB,MAAM,MAAM,GAAG,MAAM,EAAE;SACrB,OAAO,CAAC,wDAAwD,CAAC;SACjE,IAAI,CAAC,QAAQ,CAAC;SACd,KAAK,EAAE,CAAA;IAET,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACtC,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,4BAA4B,CACjD,EAAc,EACd,QAAgB;IAEhB,MAAM,MAAM,GAAG,MAAM,EAAE;SACrB,OAAO,CAAC,sDAAsD,CAAC;SAC/D,IAAI,CAAC,QAAQ,CAAC;SACd,GAAG,EAAE,CAAA;IAEP,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;AACvC,CAAC;AAED,kCAAkC;AAClC,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,EAAc,EAAE,EAAU;IACtE,MAAM,EAAE,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAA;AAClF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,EAAc,EACd,UAAgE,EAAE;IAElE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAA;IACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAA;IAElC,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,EAAE;aACrB,OAAO,CACP,gGAAgG,CAChG;aACA,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC;aACnC,GAAG,EAAE,CAAA;QACP,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAC1C,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,EAAE;SACrB,OAAO,CAAC,8EAA8E,CAAC;SACvF,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC;SACnB,GAAG,EAAE,CAAA;IAEP,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;AAC1C,CAAC;AAED,gCAAgC;AAChC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EAAc;IACtD,MAAM,MAAM,GAAG,MAAM,EAAE;SACrB,OAAO,CAAC,qDAAqD,CAAC;SAC9D,KAAK,EAAqB,CAAA;IAE5B,OAAO,MAAM,EAAE,KAAK,IAAI,CAAC,CAAA;AAC1B,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAc,EAAE,EAAU;IAC7D,MAAM,EAAE;SACN,OAAO,CACP;;iBAEc,CACd;SACA,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;SACvC,GAAG,EAAE,CAAA;AACR,CAAC;AAED,iDAAiD;AACjD,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,EAAc,EAAE,EAAU;IACrE,MAAM,EAAE;SACN,OAAO,CACP;;iBAEc,CACd;SACA,IAAI,CAAC,EAAE,CAAC;SACR,GAAG,EAAE,CAAA;AACR,CAAC;AAED,SAAS,MAAM,CAAC,GAA4B;IAC3C,OAAO;QACN,EAAE,EAAE,GAAG,CAAC,EAAY;QACpB,QAAQ,EAAE,GAAG,CAAC,QAAkB;QAChC,SAAS,EAAE,GAAG,CAAC,UAAoB,EAAE,qEAAqE;QAC1G,OAAO,EAAE,GAAG,CAAC,QAAkB;QAC/B,MAAM,EAAE,GAAG,CAAC,MAAuB;QACnC,SAAS,EAAE,GAAG,CAAC,UAAoB;QACnC,cAAc,EAAE,GAAG,CAAC,gBAAiC;QACrD,YAAY,EAAE,GAAG,CAAC,aAAuB;QACzC,SAAS,EAAE,GAAG,CAAC,UAA2B;QAC1C,OAAO,EAAE,GAAG,CAAC,OAAwB;QACrC,MAAM,EAAG,GAAG,CAAC,OAAqC,IAAI,IAAI;KAC1D,CAAA;AACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growth-labs/push",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@cloudflare/workers-types": "^4.0.0",
|
|
76
76
|
"astro": "^6.1.10",
|
|
77
77
|
"typescript": "^5.7.0",
|
|
78
|
-
"vitest": "^
|
|
78
|
+
"vitest": "^4.1.8"
|
|
79
79
|
},
|
|
80
80
|
"scripts": {
|
|
81
81
|
"build": "tsc",
|
package/src/db/schema.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,7 +2,11 @@ import { fileURLToPath } from 'node:url'
|
|
|
2
2
|
import type { AstroIntegration } from 'astro'
|
|
3
3
|
import { type PushOptions, pushOptionsSchema } from './options.js'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
function isSourceEntrypoint(): boolean {
|
|
6
|
+
return typeof import.meta.url === 'string' && import.meta.url.endsWith('.ts')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const ENTRYPOINT_EXTENSION = isSourceEntrypoint() ? '.ts' : '.js'
|
|
6
10
|
|
|
7
11
|
function resolveEntrypoint(path: string): string {
|
|
8
12
|
return fileURLToPath(new URL(`${path}${ENTRYPOINT_EXTENSION}`, import.meta.url))
|
package/src/options.ts
CHANGED
|
@@ -5,7 +5,14 @@ export const pushOptionsSchema = z.object({
|
|
|
5
5
|
vapidPrivateKey: z.string().min(1),
|
|
6
6
|
vapidSubject: z.string().min(1),
|
|
7
7
|
|
|
8
|
+
siteId: z.string().min(1),
|
|
9
|
+
|
|
8
10
|
d1Binding: z.string().default('SITE_DB'),
|
|
11
|
+
// D1 binding holding gl_push_subscriptions (subscribe/unsubscribe + producer
|
|
12
|
+
// reads). Defaults to d1Binding. Set to the realm push DB (e.g. 'MAILER_DB')
|
|
13
|
+
// on sites whose queue is drained by the central push-platform Worker, so the
|
|
14
|
+
// consumer's status writeback and the site's subscribe/produce share one DB.
|
|
15
|
+
subscriptionsD1Binding: z.string().optional(),
|
|
9
16
|
queueBinding: z.string().default('PUSH_QUEUE'),
|
|
10
17
|
|
|
11
18
|
defaults: z
|
package/src/routes/subscribe.ts
CHANGED
|
@@ -41,7 +41,8 @@ export const POST: APIRoute = async ({ request }) => {
|
|
|
41
41
|
})
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const
|
|
44
|
+
const subsBinding = config.subscriptionsD1Binding ?? config.d1Binding
|
|
45
|
+
const db = envMap[subsBinding] as D1Database
|
|
45
46
|
const existing = await findSubscriptionByEndpoint(db, endpoint)
|
|
46
47
|
if (existing) {
|
|
47
48
|
await saveSubscription(db, {
|
|
@@ -68,6 +69,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|
|
68
69
|
failureCount: 0,
|
|
69
70
|
userAgent: request.headers.get('User-Agent'),
|
|
70
71
|
country: request.headers.get('CF-IPCountry') ?? null,
|
|
72
|
+
siteId: config.siteId,
|
|
71
73
|
})
|
|
72
74
|
|
|
73
75
|
trackPushSubscription('subscribed', {
|
|
@@ -15,7 +15,8 @@ export const POST: APIRoute = async ({ request }) => {
|
|
|
15
15
|
})
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const
|
|
18
|
+
const subsBinding = config.subscriptionsD1Binding ?? config.d1Binding
|
|
19
|
+
const db = (env as Record<string, unknown>)[subsBinding] as D1Database
|
|
19
20
|
const deleted = await deleteSubscriptionByEndpoint(db, endpoint)
|
|
20
21
|
|
|
21
22
|
if (deleted) {
|
package/src/types.ts
CHANGED
|
@@ -30,6 +30,9 @@ export interface PushSubscriptionRecord {
|
|
|
30
30
|
failureCount: number
|
|
31
31
|
userAgent: string | null
|
|
32
32
|
country: string | null
|
|
33
|
+
/** Site identifier. Null in single-tenant mode; set to options.siteId in
|
|
34
|
+
* central-consumer mode so a shared subscriptions table can be scoped per site. */
|
|
35
|
+
siteId?: string | null
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
/**
|
|
@@ -105,7 +108,10 @@ export interface PushLocals {
|
|
|
105
108
|
/** Serializable config exported by the virtual module */
|
|
106
109
|
export interface PushConfig {
|
|
107
110
|
vapidPublicKey: string
|
|
111
|
+
siteId: string
|
|
108
112
|
d1Binding: string
|
|
113
|
+
/** Optional separate DB holding gl_push_subscriptions. Defaults to d1Binding. */
|
|
114
|
+
subscriptionsD1Binding?: string
|
|
109
115
|
queueBinding: string
|
|
110
116
|
defaults: {
|
|
111
117
|
icon?: string
|
package/src/utils/cleanup.ts
CHANGED
|
@@ -5,13 +5,31 @@ interface CleanupResult {
|
|
|
5
5
|
/**
|
|
6
6
|
* Remove stale or high-failure subscriptions.
|
|
7
7
|
* Call from a cron trigger or maintenance script.
|
|
8
|
+
*
|
|
9
|
+
* When `siteId` is provided the delete is scoped to that site's rows only —
|
|
10
|
+
* required in central-consumer mode where the table is shared across sites.
|
|
11
|
+
* Omit `siteId` for the back-compat single-tenant path (no filter applied).
|
|
8
12
|
*/
|
|
9
13
|
export async function cleanupStaleSubscriptions(
|
|
10
14
|
db: D1Database,
|
|
11
|
-
options: { staleAfterDays: number },
|
|
15
|
+
options: { staleAfterDays: number; siteId?: string },
|
|
12
16
|
): Promise<CleanupResult> {
|
|
13
17
|
const cutoff = Math.floor(Date.now() / 1000) - options.staleAfterDays * 86400
|
|
14
18
|
|
|
19
|
+
if (options.siteId !== undefined) {
|
|
20
|
+
const result = await db
|
|
21
|
+
.prepare(
|
|
22
|
+
`DELETE FROM gl_push_subscriptions
|
|
23
|
+
WHERE site_id = ?
|
|
24
|
+
AND ((last_delivery_at IS NOT NULL AND last_delivery_at < ?)
|
|
25
|
+
OR (last_delivery_at IS NULL AND created_at < ?)
|
|
26
|
+
OR failure_count >= 10)`,
|
|
27
|
+
)
|
|
28
|
+
.bind(options.siteId, cutoff, cutoff)
|
|
29
|
+
.run()
|
|
30
|
+
return { removed: result.meta?.changes ?? 0 }
|
|
31
|
+
}
|
|
32
|
+
|
|
15
33
|
const result = await db
|
|
16
34
|
.prepare(
|
|
17
35
|
`DELETE FROM gl_push_subscriptions
|
package/src/utils/queue.ts
CHANGED
|
@@ -55,20 +55,34 @@ export async function enqueuePushBatch(
|
|
|
55
55
|
/**
|
|
56
56
|
* Page through all subscriptions in D1 and enqueue batches as it goes.
|
|
57
57
|
* Never holds more than one page of subscriptions in memory.
|
|
58
|
+
*
|
|
59
|
+
* Central-consumer mode: pass `centralOptions.subscriptionsDb` to read from a
|
|
60
|
+
* shared realm D1 instead of `db`. A `site_id` filter is automatically applied
|
|
61
|
+
* using `context.siteId` so the site only fans out its own subscriptions.
|
|
62
|
+
*
|
|
63
|
+
* Back-compat (single-tenant): omit `centralOptions` — reads from `db` with no
|
|
64
|
+
* site_id filter, preserving existing behavior.
|
|
58
65
|
*/
|
|
59
66
|
export async function enqueuePushBatchAll(
|
|
60
67
|
db: D1Database,
|
|
61
68
|
payload: PushPayload,
|
|
62
69
|
context: EnqueueContext,
|
|
63
70
|
options: PushSendOptions = {},
|
|
71
|
+
centralOptions?: { subscriptionsDb: D1Database },
|
|
64
72
|
): Promise<{ batches: number; totalSubscriptions: number }> {
|
|
65
73
|
const batchSize = context.batchSize ?? 100
|
|
74
|
+
const subsDb = centralOptions?.subscriptionsDb ?? db
|
|
75
|
+
const siteIdFilter = centralOptions?.subscriptionsDb !== undefined ? context.siteId : undefined
|
|
66
76
|
let offset = 0
|
|
67
77
|
let batches = 0
|
|
68
78
|
let total = 0
|
|
69
79
|
|
|
70
80
|
while (true) {
|
|
71
|
-
const subs = await getAllSubscriptions(
|
|
81
|
+
const subs = await getAllSubscriptions(subsDb, {
|
|
82
|
+
limit: batchSize,
|
|
83
|
+
offset,
|
|
84
|
+
siteId: siteIdFilter,
|
|
85
|
+
})
|
|
72
86
|
if (subs.length === 0) break
|
|
73
87
|
|
|
74
88
|
const message: PushQueueMessage = {
|
package/src/utils/scheduled.ts
CHANGED
|
@@ -6,11 +6,20 @@ import { enqueuePushBatchAll } from './queue.js'
|
|
|
6
6
|
/**
|
|
7
7
|
* Context required by the scheduled push handler.
|
|
8
8
|
* The consumer provides this from their cron trigger's env bindings.
|
|
9
|
+
*
|
|
10
|
+
* Central-consumer mode: set `subscriptionsDb` to the shared realm D1 holding
|
|
11
|
+
* gl_push_subscriptions. The producer will read from that DB and filter by
|
|
12
|
+
* `siteId`, and cleanup will scope deletes to this site's rows only.
|
|
13
|
+
*
|
|
14
|
+
* Back-compat (single-tenant): omit `subscriptionsDb` — `db` is used for all
|
|
15
|
+
* operations and no site_id filter is applied.
|
|
9
16
|
*/
|
|
10
17
|
export interface ScheduledPushContext {
|
|
11
18
|
db: D1Database
|
|
12
19
|
queue: Queue
|
|
13
20
|
siteId: string
|
|
21
|
+
/** Optional separate DB holding gl_push_subscriptions (central mode). */
|
|
22
|
+
subscriptionsDb?: D1Database
|
|
14
23
|
batchSize?: number
|
|
15
24
|
cleanup: {
|
|
16
25
|
staleAfterDays: number
|
|
@@ -117,11 +126,15 @@ export async function handleScheduledPush(
|
|
|
117
126
|
|
|
118
127
|
// Step 2: Enqueue notifications if content is available
|
|
119
128
|
if (content) {
|
|
129
|
+
const centralOptions = context.subscriptionsDb
|
|
130
|
+
? { subscriptionsDb: context.subscriptionsDb }
|
|
131
|
+
: undefined
|
|
120
132
|
const enqueueResult = await enqueuePushBatchAll(
|
|
121
133
|
context.db,
|
|
122
134
|
content.payload,
|
|
123
135
|
{ queue: context.queue, siteId: context.siteId, batchSize: context.batchSize },
|
|
124
136
|
content.options ?? {},
|
|
137
|
+
centralOptions,
|
|
125
138
|
)
|
|
126
139
|
|
|
127
140
|
notification = {
|
|
@@ -139,8 +152,10 @@ export async function handleScheduledPush(
|
|
|
139
152
|
}
|
|
140
153
|
|
|
141
154
|
// Step 3: Always clean up stale subscriptions
|
|
142
|
-
const
|
|
155
|
+
const cleanupDb = context.subscriptionsDb ?? context.db
|
|
156
|
+
const cleanupResult = await cleanupStaleSubscriptions(cleanupDb, {
|
|
143
157
|
staleAfterDays: context.cleanup.staleAfterDays,
|
|
158
|
+
siteId: context.subscriptionsDb !== undefined ? context.siteId : undefined,
|
|
144
159
|
})
|
|
145
160
|
|
|
146
161
|
return {
|
|
@@ -5,8 +5,8 @@ export async function saveSubscription(db: D1Database, sub: PushSubscriptionReco
|
|
|
5
5
|
await db
|
|
6
6
|
.prepare(
|
|
7
7
|
`INSERT INTO gl_push_subscriptions
|
|
8
|
-
(id, endpoint, key_p256dh, key_auth, topics, created_at, last_delivery_at, failure_count, user_agent, country)
|
|
9
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
8
|
+
(id, endpoint, key_p256dh, key_auth, topics, created_at, last_delivery_at, failure_count, user_agent, country, site_id)
|
|
9
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
10
10
|
ON CONFLICT (endpoint) DO UPDATE SET
|
|
11
11
|
key_p256dh = excluded.key_p256dh,
|
|
12
12
|
key_auth = excluded.key_auth,
|
|
@@ -24,6 +24,7 @@ export async function saveSubscription(db: D1Database, sub: PushSubscriptionReco
|
|
|
24
24
|
sub.failureCount,
|
|
25
25
|
sub.userAgent,
|
|
26
26
|
sub.country,
|
|
27
|
+
sub.siteId ?? null,
|
|
27
28
|
)
|
|
28
29
|
.run()
|
|
29
30
|
}
|
|
@@ -59,14 +60,29 @@ export async function deleteSubscriptionById(db: D1Database, id: string): Promis
|
|
|
59
60
|
await db.prepare('DELETE FROM gl_push_subscriptions WHERE id = ?').bind(id).run()
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
/** Get subscriptions with offset-based pagination. Used by enqueuePushBatchAll.
|
|
63
|
+
/** Get subscriptions with offset-based pagination. Used by enqueuePushBatchAll.
|
|
64
|
+
*
|
|
65
|
+
* When `siteId` is provided the query adds `WHERE site_id = ?` so a site only
|
|
66
|
+
* reads its own rows from a shared (central) subscriptions table. Omit siteId
|
|
67
|
+
* for the back-compat single-tenant path (no filter applied).
|
|
68
|
+
*/
|
|
63
69
|
export async function getAllSubscriptions(
|
|
64
70
|
db: D1Database,
|
|
65
|
-
options: { limit?: number; offset?: number } = {},
|
|
71
|
+
options: { limit?: number; offset?: number; siteId?: string } = {},
|
|
66
72
|
): Promise<PushSubscriptionRecord[]> {
|
|
67
73
|
const limit = options.limit ?? 1000
|
|
68
74
|
const offset = options.offset ?? 0
|
|
69
75
|
|
|
76
|
+
if (options.siteId !== undefined) {
|
|
77
|
+
const result = await db
|
|
78
|
+
.prepare(
|
|
79
|
+
'SELECT * FROM gl_push_subscriptions WHERE site_id = ? ORDER BY created_at ASC LIMIT ? OFFSET ?',
|
|
80
|
+
)
|
|
81
|
+
.bind(options.siteId, limit, offset)
|
|
82
|
+
.all()
|
|
83
|
+
return (result.results ?? []).map(mapRow)
|
|
84
|
+
}
|
|
85
|
+
|
|
70
86
|
const result = await db
|
|
71
87
|
.prepare('SELECT * FROM gl_push_subscriptions ORDER BY created_at ASC LIMIT ? OFFSET ?')
|
|
72
88
|
.bind(limit, offset)
|
|
@@ -120,5 +136,6 @@ function mapRow(row: Record<string, unknown>): PushSubscriptionRecord {
|
|
|
120
136
|
failureCount: row.failure_count as number,
|
|
121
137
|
userAgent: row.user_agent as string | null,
|
|
122
138
|
country: row.country as string | null,
|
|
139
|
+
siteId: (row.site_id as string | null | undefined) ?? null,
|
|
123
140
|
}
|
|
124
141
|
}
|