@onesub/server 0.9.1 → 0.11.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 +86 -1
- package/dist/__tests__/entitlements.test.d.ts +14 -0
- package/dist/__tests__/entitlements.test.d.ts.map +1 -0
- package/dist/__tests__/entitlements.test.js +268 -0
- package/dist/__tests__/entitlements.test.js.map +1 -0
- package/dist/__tests__/metrics.test.d.ts +6 -0
- package/dist/__tests__/metrics.test.d.ts.map +1 -0
- package/dist/__tests__/metrics.test.js +227 -0
- package/dist/__tests__/metrics.test.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/routes/entitlements.d.ts +23 -0
- package/dist/routes/entitlements.d.ts.map +1 -0
- package/dist/routes/entitlements.js +130 -0
- package/dist/routes/entitlements.js.map +1 -0
- package/dist/routes/metrics.d.ts +18 -0
- package/dist/routes/metrics.d.ts.map +1 -0
- package/dist/routes/metrics.js +181 -0
- package/dist/routes/metrics.js.map +1 -0
- package/dist/store.d.ts +26 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +20 -2
- package/dist/store.js.map +1 -1
- package/dist/stores/postgres.d.ts +9 -0
- package/dist/stores/postgres.d.ts.map +1 -1
- package/dist/stores/postgres.js +24 -0
- package/dist/stores/postgres.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only metrics endpoints — admin-gated aggregations over Subscription +
|
|
3
|
+
* PurchaseStore data.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import express from 'express';
|
|
7
|
+
import { createOneSubMiddleware, InMemorySubscriptionStore, InMemoryPurchaseStore } from '../index.js';
|
|
8
|
+
// ── helpers ─────────────────────────────────────────────────────────────────
|
|
9
|
+
function sub(overrides) {
|
|
10
|
+
return {
|
|
11
|
+
userId: 'u',
|
|
12
|
+
productId: 'pro_monthly',
|
|
13
|
+
platform: 'apple',
|
|
14
|
+
status: 'active',
|
|
15
|
+
expiresAt: '2099-01-01T00:00:00.000Z',
|
|
16
|
+
purchasedAt: '2026-04-01T00:00:00.000Z',
|
|
17
|
+
originalTransactionId: `orig_${Math.random()}`,
|
|
18
|
+
willRenew: true,
|
|
19
|
+
...overrides,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function purchase(overrides) {
|
|
23
|
+
return {
|
|
24
|
+
userId: 'u',
|
|
25
|
+
productId: 'lifetime_pass',
|
|
26
|
+
platform: 'apple',
|
|
27
|
+
type: 'non_consumable',
|
|
28
|
+
transactionId: `tx_${Math.random()}`,
|
|
29
|
+
purchasedAt: '2026-04-01T00:00:00.000Z',
|
|
30
|
+
quantity: 1,
|
|
31
|
+
...overrides,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function spinUp(handler) {
|
|
35
|
+
return {
|
|
36
|
+
async get(path, headers) {
|
|
37
|
+
const httpServer = handler.listen(0);
|
|
38
|
+
const port = httpServer.address().port;
|
|
39
|
+
try {
|
|
40
|
+
const resp = await fetch(`http://127.0.0.1:${port}${path}`, { headers });
|
|
41
|
+
const text = await resp.text();
|
|
42
|
+
let parsed = text;
|
|
43
|
+
try {
|
|
44
|
+
parsed = JSON.parse(text);
|
|
45
|
+
}
|
|
46
|
+
catch { /* keep as text */ }
|
|
47
|
+
return { status: resp.status, body: parsed };
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
await new Promise((r) => httpServer.close(() => r()));
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function buildServer(opts) {
|
|
56
|
+
const store = new InMemorySubscriptionStore();
|
|
57
|
+
const purchaseStore = new InMemoryPurchaseStore();
|
|
58
|
+
const config = {
|
|
59
|
+
apple: { bundleId: 'com.example.app', skipJwsVerification: true },
|
|
60
|
+
database: { url: '' },
|
|
61
|
+
adminSecret: opts.adminSecret,
|
|
62
|
+
};
|
|
63
|
+
const app = express();
|
|
64
|
+
app.use(createOneSubMiddleware({ ...config, store, purchaseStore }));
|
|
65
|
+
return { store, purchaseStore, server: spinUp(app) };
|
|
66
|
+
}
|
|
67
|
+
const SECRET = 's3cr3t';
|
|
68
|
+
const AUTH = { 'x-admin-secret': SECRET };
|
|
69
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
70
|
+
// Auth gate
|
|
71
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
72
|
+
describe('Metrics auth', () => {
|
|
73
|
+
it('returns 404 (router not mounted) when adminSecret is unset', async () => {
|
|
74
|
+
const { server } = buildServer({});
|
|
75
|
+
const resp = await server.get('/onesub/metrics/active');
|
|
76
|
+
expect(resp.status).toBe(404);
|
|
77
|
+
});
|
|
78
|
+
it('returns 401 INVALID_ADMIN_SECRET when header is missing or wrong', async () => {
|
|
79
|
+
const { server } = buildServer({ adminSecret: SECRET });
|
|
80
|
+
const noHeader = await server.get('/onesub/metrics/active');
|
|
81
|
+
expect(noHeader.status).toBe(401);
|
|
82
|
+
expect(noHeader.body.errorCode).toBe('INVALID_ADMIN_SECRET');
|
|
83
|
+
const wrongHeader = await server.get('/onesub/metrics/active', { 'x-admin-secret': 'wrong' });
|
|
84
|
+
expect(wrongHeader.status).toBe(401);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
88
|
+
// /onesub/metrics/active
|
|
89
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
90
|
+
describe('GET /onesub/metrics/active', () => {
|
|
91
|
+
it('returns zeros for an empty store', async () => {
|
|
92
|
+
const { server } = buildServer({ adminSecret: SECRET });
|
|
93
|
+
const resp = await server.get('/onesub/metrics/active', AUTH);
|
|
94
|
+
expect(resp.status).toBe(200);
|
|
95
|
+
expect(resp.body).toEqual({
|
|
96
|
+
total: 0,
|
|
97
|
+
activeSubscriptions: 0,
|
|
98
|
+
gracePeriodSubscriptions: 0,
|
|
99
|
+
nonConsumablePurchases: 0,
|
|
100
|
+
byProduct: {},
|
|
101
|
+
byPlatform: {},
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
it('counts active subs + non-consumable purchases', async () => {
|
|
105
|
+
const { store, purchaseStore, server } = buildServer({ adminSecret: SECRET });
|
|
106
|
+
await store.save(sub({ userId: 'a', productId: 'pro_monthly', platform: 'apple' }));
|
|
107
|
+
await store.save(sub({ userId: 'b', productId: 'pro_yearly', platform: 'google' }));
|
|
108
|
+
await purchaseStore.savePurchase(purchase({ userId: 'c', platform: 'apple' }));
|
|
109
|
+
const resp = await server.get('/onesub/metrics/active', AUTH);
|
|
110
|
+
expect(resp.body.total).toBe(3);
|
|
111
|
+
expect(resp.body.activeSubscriptions).toBe(2);
|
|
112
|
+
expect(resp.body.nonConsumablePurchases).toBe(1);
|
|
113
|
+
expect(resp.body.byProduct).toEqual({ pro_monthly: 1, pro_yearly: 1 }); // purchases not in byProduct
|
|
114
|
+
expect(resp.body.byPlatform).toEqual({ apple: 2, google: 1 });
|
|
115
|
+
});
|
|
116
|
+
it('grace_period subs counted in activeSubscriptions AND gracePeriodSubscriptions', async () => {
|
|
117
|
+
const { store, server } = buildServer({ adminSecret: SECRET });
|
|
118
|
+
await store.save(sub({ userId: 'a', status: 'active' }));
|
|
119
|
+
await store.save(sub({ userId: 'b', status: 'grace_period' }));
|
|
120
|
+
const resp = await server.get('/onesub/metrics/active', AUTH);
|
|
121
|
+
expect(resp.body.activeSubscriptions).toBe(2);
|
|
122
|
+
expect(resp.body.gracePeriodSubscriptions).toBe(1);
|
|
123
|
+
});
|
|
124
|
+
it('on_hold / paused / canceled / expired subs NOT counted', async () => {
|
|
125
|
+
const { store, server } = buildServer({ adminSecret: SECRET });
|
|
126
|
+
for (const status of ['on_hold', 'paused', 'canceled', 'expired']) {
|
|
127
|
+
await store.save(sub({ userId: status, status }));
|
|
128
|
+
}
|
|
129
|
+
await store.save(sub({ userId: 'real_active', status: 'active' }));
|
|
130
|
+
const resp = await server.get('/onesub/metrics/active', AUTH);
|
|
131
|
+
expect(resp.body.activeSubscriptions).toBe(1);
|
|
132
|
+
});
|
|
133
|
+
it('expired-by-time subs (status=active but expiresAt < now) NOT counted', async () => {
|
|
134
|
+
const { store, server } = buildServer({ adminSecret: SECRET });
|
|
135
|
+
await store.save(sub({ userId: 'past', status: 'active', expiresAt: '2024-01-01T00:00:00.000Z' }));
|
|
136
|
+
await store.save(sub({ userId: 'future', status: 'active', expiresAt: '2099-01-01T00:00:00.000Z' }));
|
|
137
|
+
const resp = await server.get('/onesub/metrics/active', AUTH);
|
|
138
|
+
expect(resp.body.activeSubscriptions).toBe(1);
|
|
139
|
+
});
|
|
140
|
+
it('consumable purchases NOT counted (only non_consumable contribute)', async () => {
|
|
141
|
+
const { purchaseStore, server } = buildServer({ adminSecret: SECRET });
|
|
142
|
+
await purchaseStore.savePurchase(purchase({ userId: 'a', type: 'consumable' }));
|
|
143
|
+
await purchaseStore.savePurchase(purchase({ userId: 'b', type: 'non_consumable' }));
|
|
144
|
+
const resp = await server.get('/onesub/metrics/active', AUTH);
|
|
145
|
+
expect(resp.body.nonConsumablePurchases).toBe(1);
|
|
146
|
+
expect(resp.body.total).toBe(1);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
150
|
+
// /onesub/metrics/started
|
|
151
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
152
|
+
describe('GET /onesub/metrics/started', () => {
|
|
153
|
+
it('counts subs with purchasedAt within the [from, to] window', async () => {
|
|
154
|
+
const { store, server } = buildServer({ adminSecret: SECRET });
|
|
155
|
+
await store.save(sub({ userId: 'before', purchasedAt: '2026-03-15T00:00:00Z' }));
|
|
156
|
+
await store.save(sub({ userId: 'in1', purchasedAt: '2026-04-01T00:00:00Z' }));
|
|
157
|
+
await store.save(sub({ userId: 'in2', purchasedAt: '2026-04-15T00:00:00Z' }));
|
|
158
|
+
await store.save(sub({ userId: 'after', purchasedAt: '2026-05-15T00:00:00Z' }));
|
|
159
|
+
const resp = await server.get('/onesub/metrics/started?from=2026-04-01T00:00:00Z&to=2026-04-30T23:59:59Z', AUTH);
|
|
160
|
+
expect(resp.body.total).toBe(2);
|
|
161
|
+
expect(resp.body.from).toBe('2026-04-01T00:00:00Z');
|
|
162
|
+
expect(resp.body.to).toBe('2026-04-30T23:59:59Z');
|
|
163
|
+
});
|
|
164
|
+
it('aggregates by product and platform', async () => {
|
|
165
|
+
const { store, server } = buildServer({ adminSecret: SECRET });
|
|
166
|
+
await store.save(sub({ userId: 'a', productId: 'pro_monthly', platform: 'apple', purchasedAt: '2026-04-10T00:00:00Z' }));
|
|
167
|
+
await store.save(sub({ userId: 'b', productId: 'pro_yearly', platform: 'google', purchasedAt: '2026-04-20T00:00:00Z' }));
|
|
168
|
+
await store.save(sub({ userId: 'c', productId: 'pro_monthly', platform: 'google', purchasedAt: '2026-04-25T00:00:00Z' }));
|
|
169
|
+
const resp = await server.get('/onesub/metrics/started?from=2026-04-01T00:00:00Z&to=2026-04-30T23:59:59Z', AUTH);
|
|
170
|
+
expect(resp.body.byProduct).toEqual({ pro_monthly: 2, pro_yearly: 1 });
|
|
171
|
+
expect(resp.body.byPlatform).toEqual({ apple: 1, google: 2 });
|
|
172
|
+
});
|
|
173
|
+
it('400 INVALID_INPUT when from/to are missing or malformed', async () => {
|
|
174
|
+
const { server } = buildServer({ adminSecret: SECRET });
|
|
175
|
+
const missing = await server.get('/onesub/metrics/started', AUTH);
|
|
176
|
+
expect(missing.status).toBe(400);
|
|
177
|
+
const malformed = await server.get('/onesub/metrics/started?from=not-a-date&to=2026-04-30T23:59:59Z', AUTH);
|
|
178
|
+
expect(malformed.status).toBe(400);
|
|
179
|
+
const reversed = await server.get('/onesub/metrics/started?from=2026-05-01T00:00:00Z&to=2026-04-01T00:00:00Z', AUTH);
|
|
180
|
+
expect(reversed.status).toBe(400);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
184
|
+
// /onesub/metrics/expired
|
|
185
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
186
|
+
describe('GET /onesub/metrics/expired', () => {
|
|
187
|
+
it('counts only subs where status ∈ {expired, canceled} AND expiresAt in window', async () => {
|
|
188
|
+
const { store, server } = buildServer({ adminSecret: SECRET });
|
|
189
|
+
// status active but expiresAt in window — NOT counted (not actually ended)
|
|
190
|
+
await store.save(sub({ userId: 'still_active', status: 'active', expiresAt: '2026-04-15T00:00:00Z' }));
|
|
191
|
+
// status expired, expiresAt in window — counted
|
|
192
|
+
await store.save(sub({ userId: 'expired_in', status: 'expired', expiresAt: '2026-04-10T00:00:00Z' }));
|
|
193
|
+
// status canceled (refund), expiresAt in window — counted
|
|
194
|
+
await store.save(sub({ userId: 'canceled_in', status: 'canceled', expiresAt: '2026-04-20T00:00:00Z' }));
|
|
195
|
+
// status expired but expiresAt before window
|
|
196
|
+
await store.save(sub({ userId: 'expired_before', status: 'expired', expiresAt: '2026-03-01T00:00:00Z' }));
|
|
197
|
+
const resp = await server.get('/onesub/metrics/expired?from=2026-04-01T00:00:00Z&to=2026-04-30T23:59:59Z', AUTH);
|
|
198
|
+
expect(resp.body.total).toBe(2); // expired_in + canceled_in
|
|
199
|
+
});
|
|
200
|
+
it('returns 0 when nothing matches', async () => {
|
|
201
|
+
const { store, server } = buildServer({ adminSecret: SECRET });
|
|
202
|
+
await store.save(sub({ status: 'active' }));
|
|
203
|
+
const resp = await server.get('/onesub/metrics/expired?from=2020-01-01T00:00:00Z&to=2020-12-31T23:59:59Z', AUTH);
|
|
204
|
+
expect(resp.body.total).toBe(0);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
208
|
+
// Store listAll regression
|
|
209
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
210
|
+
describe('Store listAll', () => {
|
|
211
|
+
it('SubscriptionStore.listAll returns every record', async () => {
|
|
212
|
+
const store = new InMemorySubscriptionStore();
|
|
213
|
+
await store.save(sub({ userId: 'a', originalTransactionId: 't1' }));
|
|
214
|
+
await store.save(sub({ userId: 'b', originalTransactionId: 't2' }));
|
|
215
|
+
await store.save(sub({ userId: 'a', originalTransactionId: 't3' }));
|
|
216
|
+
const all = await store.listAll();
|
|
217
|
+
expect(all).toHaveLength(3);
|
|
218
|
+
});
|
|
219
|
+
it('PurchaseStore.listAll returns every record', async () => {
|
|
220
|
+
const purchaseStore = new InMemoryPurchaseStore();
|
|
221
|
+
await purchaseStore.savePurchase(purchase({ userId: 'a', transactionId: 'p1' }));
|
|
222
|
+
await purchaseStore.savePurchase(purchase({ userId: 'b', transactionId: 'p2' }));
|
|
223
|
+
const all = await purchaseStore.listAll();
|
|
224
|
+
expect(all).toHaveLength(2);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
//# sourceMappingURL=metrics.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.test.js","sourceRoot":"","sources":["../../src/__tests__/metrics.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,7 @@ export type { SubscriptionStore, PurchaseStore } from './store.js';
|
|
|
49
49
|
export { PostgresSubscriptionStore, PostgresPurchaseStore } from './stores/postgres.js';
|
|
50
50
|
export { validateAppleReceipt, fetchAppleSubscriptionStatus } from './providers/apple.js';
|
|
51
51
|
export { validateGoogleReceipt } from './providers/google.js';
|
|
52
|
+
export { evaluateEntitlement } from './routes/entitlements.js';
|
|
52
53
|
export { setLogger, log } from './logger.js';
|
|
53
54
|
export default createOneSubMiddleware;
|
|
54
55
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAUnE;;GAEG;AACH,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAChE;;;OAGG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAwC7E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,sBAAsB,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAW7F;AAGD,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAC9E,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGxF,OAAO,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAI9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAI/D,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAG7C,eAAe,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,8 @@ import { createStatusRouter } from './routes/status.js';
|
|
|
6
6
|
import { createWebhookRouter } from './routes/webhook.js';
|
|
7
7
|
import { createPurchaseRouter } from './routes/purchase.js';
|
|
8
8
|
import { createAdminRouter } from './routes/admin.js';
|
|
9
|
+
import { createEntitlementRouter } from './routes/entitlements.js';
|
|
10
|
+
import { createMetricsRouter } from './routes/metrics.js';
|
|
9
11
|
import { setLogger } from './logger.js';
|
|
10
12
|
/**
|
|
11
13
|
* Create an Express Router with all OneSub routes mounted.
|
|
@@ -46,6 +48,15 @@ export function createOneSubMiddleware(config) {
|
|
|
46
48
|
const adminRouter = createAdminRouter(config, purchaseStore);
|
|
47
49
|
if (adminRouter)
|
|
48
50
|
router.use(adminRouter);
|
|
51
|
+
// Entitlement routes — only mounted when config.entitlements is set
|
|
52
|
+
const entitlementRouter = createEntitlementRouter(config, store, purchaseStore);
|
|
53
|
+
if (entitlementRouter)
|
|
54
|
+
router.use(entitlementRouter);
|
|
55
|
+
// Metrics routes — only mounted when config.adminSecret is set (same gate
|
|
56
|
+
// as admin routes; metrics expose aggregate operational data)
|
|
57
|
+
const metricsRouter = createMetricsRouter(config, store, purchaseStore);
|
|
58
|
+
if (metricsRouter)
|
|
59
|
+
router.use(metricsRouter);
|
|
49
60
|
return router;
|
|
50
61
|
}
|
|
51
62
|
/**
|
|
@@ -71,6 +82,9 @@ export { PostgresSubscriptionStore, PostgresPurchaseStore } from './stores/postg
|
|
|
71
82
|
// Provider functions for direct (non-HTTP) usage
|
|
72
83
|
export { validateAppleReceipt, fetchAppleSubscriptionStatus } from './providers/apple.js';
|
|
73
84
|
export { validateGoogleReceipt } from './providers/google.js';
|
|
85
|
+
// Entitlement evaluator — exported so hosts can evaluate entitlements
|
|
86
|
+
// in-process (e.g. from non-HTTP background workers, custom routes).
|
|
87
|
+
export { evaluateEntitlement } from './routes/entitlements.js';
|
|
74
88
|
// Logger plumbing — expose so non-middleware callers (direct provider use)
|
|
75
89
|
// can still redirect logs.
|
|
76
90
|
export { setLogger, log } from './logger.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAmBxC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAA8B;IACnE,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEzB,0EAA0E;IAC1E,2EAA2E;IAC3E,0EAA0E;IAC1E,2DAA2D;IAC3D,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY,EAAE,CAAC;QACpG,MAAM,IAAI,KAAK,CACb,iIAAiI,CAClI,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAsB,MAAM,CAAC,KAAK,IAAI,IAAI,yBAAyB,EAAE,CAAC;IACjF,MAAM,aAAa,GAAkB,MAAM,CAAC,aAAa,IAAI,IAAI,qBAAqB,EAAE,CAAC;IAEzF,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,kFAAkF;IAClF,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAE5C,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;IACtC,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC;IAC9D,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;IAExD,6DAA6D;IAC7D,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC7D,IAAI,WAAW;QAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAEzC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAA8B;IAC/D,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IAEtB,eAAe;IACf,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QAC/B,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC;IAExC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mEAAmE;AACnE,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE9E,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAExF,iDAAiD;AACjD,OAAO,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,2EAA2E;AAC3E,2BAA2B;AAC3B,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAE7C,yCAAyC;AACzC,eAAe,sBAAsB,CAAC;AAEtC,6CAA6C;AAC7C,mEAAmE;AACnE,MAAM,MAAM,GACV,OAAO,OAAO,KAAK,WAAW;IAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI;IACvB,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;AAElF,IAAI,MAAM,EAAE,CAAC;IACX,MAAM,MAAM,GAA2B;QACrC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;YACnC,CAAC,CAAC;gBACE,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;gBACxC,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;gBAChD,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;gBAClC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;gBACxC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;aAC7C;YACH,CAAC,CAAC,SAAS;QACb,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;YACxC,CAAC,CAAC;gBACE,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;gBAC/C,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC;aAC7D;YACH,CAAC,CAAC,SAAS;QACb,QAAQ,EAAE;YACR,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE;SACvC;QACD,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;KAC7C,CAAC;IAEF,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;IAEpF,kBAAkB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QAC3C,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import type { Entitlement, EntitlementStatus, OneSubServerConfig, PurchaseInfo, SubscriptionInfo } from '@onesub/shared';
|
|
3
|
+
import type { PurchaseStore, SubscriptionStore } from '../store.js';
|
|
4
|
+
/**
|
|
5
|
+
* Evaluate a single entitlement for a user.
|
|
6
|
+
*
|
|
7
|
+
* A user is entitled when EITHER condition holds for any productId in
|
|
8
|
+
* `entitlement.productIds`:
|
|
9
|
+
* 1. an active subscription (status === active|grace_period AND
|
|
10
|
+
* expiresAt > now) for that productId exists
|
|
11
|
+
* 2. a non-consumable purchase for that productId exists
|
|
12
|
+
*
|
|
13
|
+
* Consumables are intentionally excluded — they grant a one-time resource,
|
|
14
|
+
* not an ongoing right.
|
|
15
|
+
*
|
|
16
|
+
* Subscription is preferred over purchase when both match (subs carry an
|
|
17
|
+
* expiry; non-consumables are forever) so the source field skews toward the
|
|
18
|
+
* more "interesting" signal for ops/analytics.
|
|
19
|
+
*/
|
|
20
|
+
export declare function evaluateEntitlement(userId: string, entitlement: Entitlement, store: SubscriptionStore, purchaseStore: PurchaseStore): Promise<EntitlementStatus>;
|
|
21
|
+
export declare function createEntitlementRouter(config: OneSubServerConfig, store: SubscriptionStore, purchaseStore: PurchaseStore): Router | null;
|
|
22
|
+
export type { Entitlement, EntitlementStatus, PurchaseInfo, SubscriptionInfo };
|
|
23
|
+
//# sourceMappingURL=entitlements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../../src/routes/entitlements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EACV,WAAW,EAEX,iBAAiB,EAGjB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIpE;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,iBAAiB,EACxB,aAAa,EAAE,aAAa,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAkC5B;AAKD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,iBAAiB,EACxB,aAAa,EAAE,aAAa,GAC3B,MAAM,GAAG,IAAI,CA0Ef;AAID,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { ROUTES, SUBSCRIPTION_STATUS, ONESUB_ERROR_CODE, PURCHASE_TYPE } from '@onesub/shared';
|
|
4
|
+
import { log } from '../logger.js';
|
|
5
|
+
import { sendError } from '../errors.js';
|
|
6
|
+
/**
|
|
7
|
+
* Evaluate a single entitlement for a user.
|
|
8
|
+
*
|
|
9
|
+
* A user is entitled when EITHER condition holds for any productId in
|
|
10
|
+
* `entitlement.productIds`:
|
|
11
|
+
* 1. an active subscription (status === active|grace_period AND
|
|
12
|
+
* expiresAt > now) for that productId exists
|
|
13
|
+
* 2. a non-consumable purchase for that productId exists
|
|
14
|
+
*
|
|
15
|
+
* Consumables are intentionally excluded — they grant a one-time resource,
|
|
16
|
+
* not an ongoing right.
|
|
17
|
+
*
|
|
18
|
+
* Subscription is preferred over purchase when both match (subs carry an
|
|
19
|
+
* expiry; non-consumables are forever) so the source field skews toward the
|
|
20
|
+
* more "interesting" signal for ops/analytics.
|
|
21
|
+
*/
|
|
22
|
+
export async function evaluateEntitlement(userId, entitlement, store, purchaseStore) {
|
|
23
|
+
const productIdSet = new Set(entitlement.productIds);
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
// 1. Check subscriptions first (richer signal — has expiry).
|
|
26
|
+
const subs = await store.getAllByUserId(userId);
|
|
27
|
+
for (const sub of subs) {
|
|
28
|
+
if (!productIdSet.has(sub.productId))
|
|
29
|
+
continue;
|
|
30
|
+
const statusAllows = sub.status === SUBSCRIPTION_STATUS.ACTIVE ||
|
|
31
|
+
sub.status === SUBSCRIPTION_STATUS.GRACE_PERIOD;
|
|
32
|
+
if (!statusAllows)
|
|
33
|
+
continue;
|
|
34
|
+
if (new Date(sub.expiresAt).getTime() <= now)
|
|
35
|
+
continue;
|
|
36
|
+
return {
|
|
37
|
+
active: true,
|
|
38
|
+
source: 'subscription',
|
|
39
|
+
productId: sub.productId,
|
|
40
|
+
expiresAt: sub.expiresAt,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// 2. Check non-consumable purchases.
|
|
44
|
+
const purchases = await purchaseStore.getPurchasesByUserId(userId);
|
|
45
|
+
for (const p of purchases) {
|
|
46
|
+
if (p.type !== PURCHASE_TYPE.NON_CONSUMABLE)
|
|
47
|
+
continue;
|
|
48
|
+
if (!productIdSet.has(p.productId))
|
|
49
|
+
continue;
|
|
50
|
+
return {
|
|
51
|
+
active: true,
|
|
52
|
+
source: 'purchase',
|
|
53
|
+
productId: p.productId,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return { active: false, source: null };
|
|
57
|
+
}
|
|
58
|
+
const userIdSchema = z.string().min(1).max(256);
|
|
59
|
+
const entitlementIdSchema = z.string().min(1).max(128);
|
|
60
|
+
export function createEntitlementRouter(config, store, purchaseStore) {
|
|
61
|
+
// Only mount when entitlements are configured. Without this guard, the
|
|
62
|
+
// routes would always 503 — better to make the absence loud (404 from
|
|
63
|
+
// express) so misconfiguration is obvious during dev.
|
|
64
|
+
if (!config.entitlements || Object.keys(config.entitlements).length === 0) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const entitlements = config.entitlements;
|
|
68
|
+
const router = Router();
|
|
69
|
+
/**
|
|
70
|
+
* GET /onesub/entitlement?userId=&id=premium
|
|
71
|
+
* Single entitlement check.
|
|
72
|
+
*/
|
|
73
|
+
router.get(ROUTES.ENTITLEMENT, async (req, res) => {
|
|
74
|
+
let userId;
|
|
75
|
+
let id;
|
|
76
|
+
try {
|
|
77
|
+
userId = userIdSchema.parse(req.query['userId']);
|
|
78
|
+
id = entitlementIdSchema.parse(req.query['id']);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId and id are required');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const entitlement = entitlements[id];
|
|
85
|
+
if (!entitlement) {
|
|
86
|
+
sendError(res, 404, ONESUB_ERROR_CODE.ENTITLEMENT_NOT_FOUND, `Unknown entitlement: ${id}`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const status = await evaluateEntitlement(userId, entitlement, store, purchaseStore);
|
|
91
|
+
const response = { id, ...status };
|
|
92
|
+
res.status(200).json(response);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
log.error('[onesub/entitlement] evaluation error:', err);
|
|
96
|
+
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error');
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
/**
|
|
100
|
+
* GET /onesub/entitlements?userId=
|
|
101
|
+
* Evaluate every configured entitlement in one round-trip — useful on
|
|
102
|
+
* app launch / login when the host wants the full entitlement map.
|
|
103
|
+
*/
|
|
104
|
+
router.get(ROUTES.ENTITLEMENTS, async (req, res) => {
|
|
105
|
+
let userId;
|
|
106
|
+
try {
|
|
107
|
+
userId = userIdSchema.parse(req.query['userId']);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
sendError(res, 400, ONESUB_ERROR_CODE.INVALID_INPUT, 'userId is required');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const entries = await Promise.all(Object.entries(entitlements).map(async ([id, entitlement]) => {
|
|
115
|
+
const status = await evaluateEntitlement(userId, entitlement, store, purchaseStore);
|
|
116
|
+
return [id, status];
|
|
117
|
+
}));
|
|
118
|
+
const response = {
|
|
119
|
+
entitlements: Object.fromEntries(entries),
|
|
120
|
+
};
|
|
121
|
+
res.status(200).json(response);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
log.error('[onesub/entitlements] evaluation error:', err);
|
|
125
|
+
sendError(res, 500, ONESUB_ERROR_CODE.STORE_ERROR, 'Internal server error', { entitlements: {} });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
return router;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=entitlements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements.js","sourceRoot":"","sources":["../../src/routes/entitlements.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import type { OneSubServerConfig, PurchaseInfo, SubscriptionInfo } from '@onesub/shared';
|
|
3
|
+
import type { PurchaseStore, SubscriptionStore } from '../store.js';
|
|
4
|
+
/**
|
|
5
|
+
* Read-only aggregate metrics endpoints — gated behind `config.adminSecret`.
|
|
6
|
+
* Returns count-based metrics only (active count, started count, expired count).
|
|
7
|
+
*
|
|
8
|
+
* Revenue metrics (MRR, ARR, LTV) require per-product price configuration the
|
|
9
|
+
* server doesn't currently track; deferred to a follow-up release that adds
|
|
10
|
+
* `config.products: { 'pro_monthly': { price: 9.99, currency: 'USD' } }`.
|
|
11
|
+
*
|
|
12
|
+
* Aggregation strategy: pulls every record via `store.listAll()` and reduces
|
|
13
|
+
* in memory. Fine up to ~100k records; large deployments should optimise the
|
|
14
|
+
* Postgres path with SQL aggregates (separate PR).
|
|
15
|
+
*/
|
|
16
|
+
export declare function createMetricsRouter(config: OneSubServerConfig, store: SubscriptionStore, purchaseStore: PurchaseStore): Router | null;
|
|
17
|
+
export type { PurchaseInfo, SubscriptionInfo };
|
|
18
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/routes/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,OAAO,KAAK,EAGV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAOxB,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMpE;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,iBAAiB,EACxB,aAAa,EAAE,aAAa,GAC3B,MAAM,GAAG,IAAI,CAiLf;AAED,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
|