@onesub/server 0.11.3 → 0.12.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/README.md +119 -2
- package/dist/__tests__/admin-customer-detail.test.d.ts +10 -0
- package/dist/__tests__/admin-customer-detail.test.d.ts.map +1 -0
- package/dist/__tests__/admin-customer-detail.test.js +146 -0
- package/dist/__tests__/admin-customer-detail.test.js.map +1 -0
- package/dist/__tests__/cache.test.d.ts +2 -0
- package/dist/__tests__/cache.test.d.ts.map +1 -0
- package/dist/__tests__/cache.test.js +34 -0
- package/dist/__tests__/cache.test.js.map +1 -0
- package/dist/__tests__/metrics.test.js +57 -0
- package/dist/__tests__/metrics.test.js.map +1 -1
- package/dist/__tests__/openapi.test.d.ts +2 -0
- package/dist/__tests__/openapi.test.d.ts.map +1 -0
- package/dist/__tests__/openapi.test.js +32 -0
- package/dist/__tests__/openapi.test.js.map +1 -0
- package/dist/__tests__/redis-store.test.d.ts +2 -0
- package/dist/__tests__/redis-store.test.d.ts.map +1 -0
- package/dist/__tests__/redis-store.test.js +117 -0
- package/dist/__tests__/redis-store.test.js.map +1 -0
- package/dist/__tests__/webhook-events.test.d.ts +2 -0
- package/dist/__tests__/webhook-events.test.d.ts.map +1 -0
- package/dist/__tests__/webhook-events.test.js +36 -0
- package/dist/__tests__/webhook-events.test.js.map +1 -0
- package/dist/__tests__/webhook-queue.test.d.ts +2 -0
- package/dist/__tests__/webhook-queue.test.d.ts.map +1 -0
- package/dist/__tests__/webhook-queue.test.js +26 -0
- package/dist/__tests__/webhook-queue.test.js.map +1 -0
- package/dist/cache.d.ts +32 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +43 -0
- package/dist/cache.js.map +1 -0
- package/dist/index.cjs +3114 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3072 -118
- package/dist/index.js.map +1 -1
- package/dist/openapi.d.ts +41 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +196 -0
- package/dist/openapi.js.map +1 -0
- package/dist/providers/apple.d.ts +1 -1
- package/dist/providers/apple.d.ts.map +1 -1
- package/dist/providers/apple.js +45 -34
- package/dist/providers/apple.js.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +49 -20
- package/dist/providers/google.js.map +1 -1
- package/dist/routes/admin.d.ts +6 -1
- package/dist/routes/admin.d.ts.map +1 -1
- package/dist/routes/admin.js +87 -1
- package/dist/routes/admin.js.map +1 -1
- package/dist/routes/metrics.d.ts.map +1 -1
- package/dist/routes/metrics.js +57 -1
- package/dist/routes/metrics.js.map +1 -1
- package/dist/routes/webhook.d.ts +2 -1
- package/dist/routes/webhook.d.ts.map +1 -1
- package/dist/routes/webhook.js +21 -1
- package/dist/routes/webhook.js.map +1 -1
- package/dist/stores/redis.d.ts +60 -0
- package/dist/stores/redis.d.ts.map +1 -0
- package/dist/stores/redis.js +255 -0
- package/dist/stores/redis.js.map +1 -0
- package/dist/stores/schema.d.ts.map +1 -1
- package/dist/stores/schema.js +10 -0
- package/dist/stores/schema.js.map +1 -1
- package/dist/tracing.d.ts +32 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +74 -0
- package/dist/tracing.js.map +1 -0
- package/dist/webhook-events.d.ts +58 -0
- package/dist/webhook-events.d.ts.map +1 -0
- package/dist/webhook-events.js +57 -0
- package/dist/webhook-events.js.map +1 -0
- package/dist/webhook-queue.d.ts +92 -0
- package/dist/webhook-queue.d.ts.map +1 -0
- package/dist/webhook-queue.js +108 -0
- package/dist/webhook-queue.js.map +1 -0
- package/package.json +33 -4
- package/sql/schema.sql +10 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronous, in-process implementation. Default — no extra infra required.
|
|
3
|
+
* Handler runs inside the HTTP request, so route latency = handler latency.
|
|
4
|
+
*
|
|
5
|
+
* Failures are NOT retried — the original Apple/Google source retry policy
|
|
6
|
+
* is the durability layer (4xx = no retry, 5xx = source retries). This
|
|
7
|
+
* matches the pre-queue behavior.
|
|
8
|
+
*/
|
|
9
|
+
export class InProcessWebhookQueue {
|
|
10
|
+
handler = null;
|
|
11
|
+
setHandler(handler) {
|
|
12
|
+
this.handler = handler;
|
|
13
|
+
}
|
|
14
|
+
async enqueue(job) {
|
|
15
|
+
if (!this.handler) {
|
|
16
|
+
throw new Error('[onesub] webhook queue has no handler registered');
|
|
17
|
+
}
|
|
18
|
+
await this.handler(job);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class BullMQWebhookQueue {
|
|
22
|
+
queueName;
|
|
23
|
+
maxAttempts;
|
|
24
|
+
backoffMs;
|
|
25
|
+
concurrency;
|
|
26
|
+
connection;
|
|
27
|
+
// Lazy-loaded so `bullmq` doesn't have to be installed unless this class
|
|
28
|
+
// is instantiated.
|
|
29
|
+
queuePromise = null;
|
|
30
|
+
workerPromise = null;
|
|
31
|
+
handler = null;
|
|
32
|
+
constructor(opts) {
|
|
33
|
+
this.connection = opts.connection;
|
|
34
|
+
this.queueName = opts.queueName ?? 'onesub-webhooks';
|
|
35
|
+
this.maxAttempts = opts.maxAttempts ?? 5;
|
|
36
|
+
this.backoffMs = opts.backoffMs ?? 1000;
|
|
37
|
+
this.concurrency = opts.concurrency ?? 4;
|
|
38
|
+
}
|
|
39
|
+
async getBullMQ() {
|
|
40
|
+
return import('bullmq').catch(() => {
|
|
41
|
+
throw new Error('[onesub] BullMQWebhookQueue requires the `bullmq` package. Run: npm install bullmq');
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
getQueue() {
|
|
45
|
+
if (!this.queuePromise) {
|
|
46
|
+
this.queuePromise = (async () => {
|
|
47
|
+
const { Queue } = await this.getBullMQ();
|
|
48
|
+
return new Queue(this.queueName, { connection: this.connection });
|
|
49
|
+
})();
|
|
50
|
+
}
|
|
51
|
+
return this.queuePromise;
|
|
52
|
+
}
|
|
53
|
+
setHandler(handler) {
|
|
54
|
+
this.handler = handler;
|
|
55
|
+
if (!this.workerPromise) {
|
|
56
|
+
this.workerPromise = (async () => {
|
|
57
|
+
const { Worker } = await this.getBullMQ();
|
|
58
|
+
return new Worker(this.queueName, async (job) => {
|
|
59
|
+
if (!this.handler)
|
|
60
|
+
throw new Error('[onesub] handler not set');
|
|
61
|
+
await this.handler(job.data);
|
|
62
|
+
}, {
|
|
63
|
+
connection: this.connection,
|
|
64
|
+
concurrency: this.concurrency,
|
|
65
|
+
});
|
|
66
|
+
})();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async enqueue(job) {
|
|
70
|
+
const queue = await this.getQueue();
|
|
71
|
+
await queue.add('webhook', job, {
|
|
72
|
+
attempts: this.maxAttempts,
|
|
73
|
+
backoff: { type: 'exponential', delay: this.backoffMs },
|
|
74
|
+
removeOnFail: false,
|
|
75
|
+
removeOnComplete: { age: 24 * 60 * 60, count: 1000 },
|
|
76
|
+
jobId: `${job.provider}:${job.eventId}`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async listDeadLetters() {
|
|
80
|
+
const queue = await this.getQueue();
|
|
81
|
+
const failed = await queue.getFailed();
|
|
82
|
+
return failed.map((j) => ({
|
|
83
|
+
id: String(j.id),
|
|
84
|
+
job: j.data,
|
|
85
|
+
attempts: j.attemptsMade,
|
|
86
|
+
lastError: j.failedReason ?? 'unknown',
|
|
87
|
+
failedAt: new Date(j.finishedOn ?? Date.now()).toISOString(),
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
async replayDeadLetter(id) {
|
|
91
|
+
const queue = await this.getQueue();
|
|
92
|
+
const job = await queue.getJob(id);
|
|
93
|
+
if (!job)
|
|
94
|
+
throw new Error(`[onesub] dead-letter job ${id} not found`);
|
|
95
|
+
await job.retry();
|
|
96
|
+
}
|
|
97
|
+
async close() {
|
|
98
|
+
if (this.workerPromise) {
|
|
99
|
+
const worker = await this.workerPromise;
|
|
100
|
+
await worker.close();
|
|
101
|
+
}
|
|
102
|
+
if (this.queuePromise) {
|
|
103
|
+
const queue = await this.queuePromise;
|
|
104
|
+
await queue.close();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=webhook-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-queue.js","sourceRoot":"","sources":["../src/webhook-queue.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onesub/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Server-side receipt validation middleware for react-native-iap. Apple StoreKit 2 + Google Play Billing. One line.",
|
|
5
|
-
"main": "dist/index.
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
7
8
|
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"sideEffects": false,
|
|
8
17
|
"files": [
|
|
9
18
|
"dist",
|
|
10
19
|
"sql"
|
|
11
20
|
],
|
|
12
21
|
"scripts": {
|
|
13
|
-
"build": "
|
|
22
|
+
"build": "npm run build:types && npm run build:bundle",
|
|
23
|
+
"build:types": "tsc --emitDeclarationOnly",
|
|
24
|
+
"build:bundle": "tsup",
|
|
14
25
|
"dev": "tsc --watch",
|
|
26
|
+
"size": "size-limit",
|
|
15
27
|
"start": "node dist/index.js"
|
|
16
28
|
},
|
|
17
29
|
"keywords": [
|
|
@@ -41,24 +53,41 @@
|
|
|
41
53
|
},
|
|
42
54
|
"license": "MIT",
|
|
43
55
|
"dependencies": {
|
|
44
|
-
"@onesub/shared": "0.7.
|
|
56
|
+
"@onesub/shared": "0.7.5",
|
|
45
57
|
"jose": "^6.2.2",
|
|
46
58
|
"zod": "^4.3.6"
|
|
47
59
|
},
|
|
48
60
|
"peerDependencies": {
|
|
61
|
+
"@opentelemetry/api": ">=1.7.0",
|
|
62
|
+
"bullmq": ">=5.0.0",
|
|
49
63
|
"express": "^4.17.0 || ^5.0.0",
|
|
64
|
+
"ioredis": ">=5.0.0",
|
|
50
65
|
"pg": ">=8.0.0"
|
|
51
66
|
},
|
|
52
67
|
"peerDependenciesMeta": {
|
|
68
|
+
"@opentelemetry/api": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"bullmq": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"ioredis": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
53
77
|
"pg": {
|
|
54
78
|
"optional": true
|
|
55
79
|
}
|
|
56
80
|
},
|
|
57
81
|
"devDependencies": {
|
|
82
|
+
"@size-limit/file": "^11.1.6",
|
|
58
83
|
"@types/express": "^5.0.6",
|
|
84
|
+
"@types/ioredis-mock": "^8.2.5",
|
|
59
85
|
"@types/node": "^20.17.0",
|
|
60
86
|
"@types/pg": "^8.20.0",
|
|
61
87
|
"express": "^5.2.1",
|
|
88
|
+
"ioredis-mock": "^8.9.0",
|
|
89
|
+
"size-limit": "^11.1.6",
|
|
90
|
+
"tsup": "^8.3.5",
|
|
62
91
|
"typescript": "^5.7.0"
|
|
63
92
|
},
|
|
64
93
|
"engines": {
|
package/sql/schema.sql
CHANGED
|
@@ -32,6 +32,16 @@ CREATE TABLE IF NOT EXISTS onesub_subscriptions (
|
|
|
32
32
|
CREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_user_id
|
|
33
33
|
ON onesub_subscriptions (user_id, updated_at DESC);
|
|
34
34
|
|
|
35
|
+
-- Filter-helper indexes for /onesub/admin/subscriptions. Each filter column
|
|
36
|
+
-- is paired with updated_at DESC so the planner can serve "latest matching"
|
|
37
|
+
-- without sorting the full table once row count grows.
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_status_updated
|
|
39
|
+
ON onesub_subscriptions (status, updated_at DESC);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_platform_updated
|
|
41
|
+
ON onesub_subscriptions (platform, updated_at DESC);
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_onesub_subscriptions_product
|
|
43
|
+
ON onesub_subscriptions (product_id, updated_at DESC);
|
|
44
|
+
|
|
35
45
|
-- Backfill columns for installs that already created the table from an older
|
|
36
46
|
-- schema. Safe to re-run.
|
|
37
47
|
ALTER TABLE onesub_subscriptions ADD COLUMN IF NOT EXISTS linked_purchase_token TEXT;
|