@tumbaland/backend-core 1.24.0 → 1.26.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/dist/app/createBaseApp.d.ts +24 -0
- package/dist/app/createBaseApp.d.ts.map +1 -1
- package/dist/app/createBaseApp.js +20 -1
- package/dist/app/createBaseApp.js.map +1 -1
- package/dist/entitlements/client.d.ts.map +1 -1
- package/dist/entitlements/client.js +17 -1
- package/dist/entitlements/client.js.map +1 -1
- package/dist/entitlements/index.d.ts +3 -2
- package/dist/entitlements/index.d.ts.map +1 -1
- package/dist/entitlements/index.js +5 -4
- package/dist/entitlements/index.js.map +1 -1
- package/dist/entitlements/middleware.d.ts.map +1 -1
- package/dist/entitlements/middleware.js +8 -19
- package/dist/entitlements/middleware.js.map +1 -1
- package/dist/entitlements/reconcile.d.ts +74 -0
- package/dist/entitlements/reconcile.d.ts.map +1 -0
- package/dist/entitlements/reconcile.js +297 -0
- package/dist/entitlements/reconcile.js.map +1 -0
- package/dist/entitlements/types.d.ts +0 -8
- package/dist/entitlements/types.d.ts.map +1 -1
- package/dist/entitlements/usage.d.ts +1 -1
- package/dist/entitlements/usage.d.ts.map +1 -1
- package/dist/entitlements/usage.js +11 -20
- package/dist/entitlements/usage.js.map +1 -1
- package/dist/groups/client.d.ts +27 -0
- package/dist/groups/client.d.ts.map +1 -0
- package/dist/groups/client.js +153 -0
- package/dist/groups/client.js.map +1 -0
- package/dist/groups/index.d.ts +5 -0
- package/dist/groups/index.d.ts.map +1 -0
- package/dist/groups/index.js +10 -0
- package/dist/groups/index.js.map +1 -0
- package/dist/groups/subject.d.ts +30 -0
- package/dist/groups/subject.d.ts.map +1 -0
- package/dist/groups/subject.js +52 -0
- package/dist/groups/subject.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/app/createBaseApp.test.ts +31 -0
- package/src/app/createBaseApp.ts +28 -1
- package/src/entitlements/client.test.ts +30 -0
- package/src/entitlements/client.ts +18 -1
- package/src/entitlements/index.ts +4 -2
- package/src/entitlements/middleware.test.ts +9 -30
- package/src/entitlements/middleware.ts +8 -19
- package/src/entitlements/reconcile.test.ts +333 -0
- package/src/entitlements/reconcile.ts +384 -0
- package/src/entitlements/types.ts +0 -9
- package/src/entitlements/usage.test.ts +27 -31
- package/src/entitlements/usage.ts +12 -22
- package/src/groups/client.test.ts +215 -0
- package/src/groups/client.ts +182 -0
- package/src/groups/index.ts +5 -0
- package/src/groups/subject.test.ts +85 -0
- package/src/groups/subject.ts +50 -0
- package/src/index.ts +3 -0
- package/dist/entitlements/mode.d.ts +0 -10
- package/dist/entitlements/mode.d.ts.map +0 -1
- package/dist/entitlements/mode.js +0 -32
- package/dist/entitlements/mode.js.map +0 -1
- package/src/entitlements/mode.test.ts +0 -50
- package/src/entitlements/mode.ts +0 -28
|
@@ -18,6 +18,12 @@ export interface CreateBaseAppOptions {
|
|
|
18
18
|
tracing?: boolean;
|
|
19
19
|
/** Use requestLoggerWithMetrics (adds Prometheus histograms) instead of the plain requestLogger. Default true. */
|
|
20
20
|
metrics?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Value for Express's `trust proxy`. Defaults to trusting private/loopback
|
|
23
|
+
* addresses — see the note on tumbaland-proxy below. Pass `false` for a
|
|
24
|
+
* service exposed directly to clients with no proxy in front of it.
|
|
25
|
+
*/
|
|
26
|
+
trustProxy?: Parameters<Express['set']>[1];
|
|
21
27
|
}
|
|
22
28
|
/**
|
|
23
29
|
* Assembles the middleware stack every service was hand-rolling in its own
|
|
@@ -39,6 +45,24 @@ export interface CreateBaseAppOptions {
|
|
|
39
45
|
* Services with non-standard body parsing (a webhook needing the raw body,
|
|
40
46
|
* or conditional parsing per-route) should pass `parseJson`/`parseCookies:
|
|
41
47
|
* false` and mount those themselves at the exact point they need to.
|
|
48
|
+
*
|
|
49
|
+
* `trust proxy` is on by default because nothing here is reached directly:
|
|
50
|
+
* every service sits behind tumbaland-proxy, which sets `X-Forwarded-For`.
|
|
51
|
+
* Left at Express's default (`false`) that header is ignored, which
|
|
52
|
+
* express-rate-limit reports as `ERR_ERL_UNEXPECTED_X_FORWARDED_FOR` — and
|
|
53
|
+
* it is right to: the limiter would then key every request in the cluster on
|
|
54
|
+
* the proxy's own container IP, so one busy client could exhaust the window
|
|
55
|
+
* for everyone.
|
|
56
|
+
*
|
|
57
|
+
* The value is `loopback, linklocal, uniquelocal` (private ranges) rather
|
|
58
|
+
* than a hop count, because the chain length differs per ingress path: a
|
|
59
|
+
* direct hit arrives as `client` + the proxy's own socket address, while
|
|
60
|
+
* traffic through the Cloudflare tunnel arrives as `client, 127.0.0.1` (the
|
|
61
|
+
* local cloudflared) + that same socket address. Express walks the chain
|
|
62
|
+
* right-to-left and stops at the first address it does not trust, so both
|
|
63
|
+
* paths land on the real client; a fixed number would be wrong for one of
|
|
64
|
+
* them. All the intermediaries are private addresses and the client is not,
|
|
65
|
+
* which is exactly the split this preset encodes.
|
|
42
66
|
*/
|
|
43
67
|
export declare function createBaseApp(options?: CreateBaseAppOptions): Express;
|
|
44
68
|
//# sourceMappingURL=createBaseApp.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createBaseApp.d.ts","sourceRoot":"","sources":["../../src/app/createBaseApp.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK3D,OAAO,EAAwB,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAI3F,MAAM,WAAW,oBAAoB;IACnC,8CAA8C;IAC9C,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC;IACrC,4IAA4I;IAC5I,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6FAA6F;IAC7F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kHAAkH;IAClH,OAAO,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"createBaseApp.d.ts","sourceRoot":"","sources":["../../src/app/createBaseApp.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK3D,OAAO,EAAwB,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAI3F,MAAM,WAAW,oBAAoB;IACnC,8CAA8C;IAC9C,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC;IACrC,4IAA4I;IAC5I,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6FAA6F;IAC7F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kHAAkH;IAClH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CA2BzE"}
|
|
@@ -32,10 +32,29 @@ const healthController_1 = require("../health/healthController");
|
|
|
32
32
|
* Services with non-standard body parsing (a webhook needing the raw body,
|
|
33
33
|
* or conditional parsing per-route) should pass `parseJson`/`parseCookies:
|
|
34
34
|
* false` and mount those themselves at the exact point they need to.
|
|
35
|
+
*
|
|
36
|
+
* `trust proxy` is on by default because nothing here is reached directly:
|
|
37
|
+
* every service sits behind tumbaland-proxy, which sets `X-Forwarded-For`.
|
|
38
|
+
* Left at Express's default (`false`) that header is ignored, which
|
|
39
|
+
* express-rate-limit reports as `ERR_ERL_UNEXPECTED_X_FORWARDED_FOR` — and
|
|
40
|
+
* it is right to: the limiter would then key every request in the cluster on
|
|
41
|
+
* the proxy's own container IP, so one busy client could exhaust the window
|
|
42
|
+
* for everyone.
|
|
43
|
+
*
|
|
44
|
+
* The value is `loopback, linklocal, uniquelocal` (private ranges) rather
|
|
45
|
+
* than a hop count, because the chain length differs per ingress path: a
|
|
46
|
+
* direct hit arrives as `client` + the proxy's own socket address, while
|
|
47
|
+
* traffic through the Cloudflare tunnel arrives as `client, 127.0.0.1` (the
|
|
48
|
+
* local cloudflared) + that same socket address. Express walks the chain
|
|
49
|
+
* right-to-left and stops at the first address it does not trust, so both
|
|
50
|
+
* paths land on the real client; a fixed number would be wrong for one of
|
|
51
|
+
* them. All the intermediaries are private addresses and the client is not,
|
|
52
|
+
* which is exactly the split this preset encodes.
|
|
35
53
|
*/
|
|
36
54
|
function createBaseApp(options = {}) {
|
|
37
|
-
const { corsOptions, rateLimiter = security_1.standardRateLimiter, parseJson = true, parseCookies = true, tracing = true, metrics = true } = options;
|
|
55
|
+
const { corsOptions, rateLimiter = security_1.standardRateLimiter, parseJson = true, parseCookies = true, tracing = true, metrics = true, trustProxy = 'loopback, linklocal, uniquelocal' } = options;
|
|
38
56
|
const app = (0, express_1.default)();
|
|
57
|
+
app.set('trust proxy', trustProxy);
|
|
39
58
|
app.get('/health/live', healthController_1.healthLive);
|
|
40
59
|
app.use(security_1.securityHeaders);
|
|
41
60
|
if (rateLimiter)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createBaseApp.js","sourceRoot":"","sources":["../../src/app/createBaseApp.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"createBaseApp.js","sourceRoot":"","sources":["../../src/app/createBaseApp.ts"],"names":[],"mappings":";;;;;AA0EA,sCA2BC;AArGD,sDAA2D;AAC3D,kEAAyC;AACzC,sDAA6D;AAC7D,wCAA+C;AAC/C,+DAAsF;AACtF,iEAA2F;AAC3F,qDAA8E;AAC9E,iEAAwD;AA4BxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,SAAgB,aAAa,CAAC,UAAgC,EAAE;IAC9D,MAAM,EACJ,WAAW,EACX,WAAW,GAAG,8BAAmB,EACjC,SAAS,GAAG,IAAI,EAChB,YAAY,GAAG,IAAI,EACnB,OAAO,GAAG,IAAI,EACd,OAAO,GAAG,IAAI,EACd,UAAU,GAAG,kCAAkC,EAChD,GAAG,OAAO,CAAC;IAEZ,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;IAEtB,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IAEnC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,6BAAU,CAAC,CAAC;IAEpC,GAAG,CAAC,GAAG,CAAC,0BAAe,CAAC,CAAC;IACzB,IAAI,WAAW;QAAE,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACtC,GAAG,CAAC,GAAG,CAAC,mCAAqB,CAAC,CAAC;IAC/B,IAAI,OAAO;QAAE,GAAG,CAAC,GAAG,CAAC,2BAAiB,CAAC,CAAC;IACxC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,wCAAwB,CAAC,CAAC,CAAC,6BAAa,CAAC,CAAC;IAC5D,GAAG,CAAC,GAAG,CAAC,IAAA,qCAAoB,EAAC,WAAW,CAAC,CAAC,CAAC;IAC3C,IAAI,YAAY;QAAE,GAAG,CAAC,GAAG,CAAC,IAAA,uBAAY,GAAE,CAAC,CAAC;IAC1C,IAAI,SAAS;QAAE,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAEvC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/entitlements/client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/entitlements/client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAuIvC;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAoB1E;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE1D;AAED,qFAAqF;AACrF,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C"}
|
|
@@ -58,8 +58,24 @@ function fallbackEntitlements(email) {
|
|
|
58
58
|
stale: true
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Prefers the container-network address over the public hostname.
|
|
63
|
+
*
|
|
64
|
+
* `PAYMENT_API_URL` cannot simply be pointed inward: payment-front's entrypoint
|
|
65
|
+
* templates that same variable into the `config.json` the *browser* downloads,
|
|
66
|
+
* so a Docker-internal value there breaks the subscription and checkout pages.
|
|
67
|
+
* One variable, two consumers with incompatible needs — this is the server-side
|
|
68
|
+
* half of it.
|
|
69
|
+
*
|
|
70
|
+
* Falling back keeps local dev (where only `PAYMENT_API_URL` is set) and any
|
|
71
|
+
* un-migrated deployment working; it just takes the long way round, out through
|
|
72
|
+
* the proxy and back, carrying the internal token across the public edge.
|
|
73
|
+
*/
|
|
74
|
+
function internalPaymentUrl() {
|
|
75
|
+
return process.env.PAYMENT_INTERNAL_URL || process.env.PAYMENT_API_URL;
|
|
76
|
+
}
|
|
61
77
|
async function fetchEntitlements(email) {
|
|
62
|
-
const baseUrl =
|
|
78
|
+
const baseUrl = internalPaymentUrl();
|
|
63
79
|
const token = process.env.INTERNAL_SERVICE_TOKEN;
|
|
64
80
|
const serviceId = process.env.SERVICE_NAME || 'unknown-service';
|
|
65
81
|
if (!baseUrl || !token) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/entitlements/client.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/entitlements/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export { UNLIMITED, FALLBACK_PLAN, STAFF_PLAN, applyLimits, METER_KEYS, FEATURE_KEYS, METER_DEFINITIONS, FEATURE_DEFINITIONS, DEFAULT_PLAN_LIMITS, ENTITLEMENT_CATALOG, resolveLimits, normalizeLimits, meterPeriod, isMeterKey, isFeatureKey, fitsWithin } from './definitions';
|
|
2
2
|
export type { MeterKey, FeatureKey, MeterPeriod, MeterDefinition, FeatureDefinition, PlanLimits, PartialPlanLimits } from './definitions';
|
|
3
|
-
export type { Entitlements
|
|
4
|
-
export { getEntitlementsMode, resetEntitlementsModeWarning } from './mode';
|
|
3
|
+
export type { Entitlements } from './types';
|
|
5
4
|
export { UsageMeter, currentPeriod } from './UsageMeter';
|
|
6
5
|
export type { IUsageMeter } from './UsageMeter';
|
|
7
6
|
export { getUsage, getUsageSnapshot, checkQuota, consumeQuota, releaseQuota, setUsage } from './usage';
|
|
8
7
|
export type { QuotaRequest } from './usage';
|
|
8
|
+
export { reconcileUsageMeters, runUsageReconciliation, RECONCILED_METERS } from './reconcile';
|
|
9
|
+
export type { ReconcileOptions, ReconcileReport, MeterChange, OrphanedMeter } from './reconcile';
|
|
9
10
|
export { getEntitlements, invalidateEntitlements, clearEntitlementsCache } from './client';
|
|
10
11
|
export { loadEntitlements, requireEntitlement, requireQuota, retentionFloor } from './middleware';
|
|
11
12
|
export type { EntitlementOptions, QuotaOptions, SubjectResolver } from './middleware';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/entitlements/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,aAAa,EACb,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,EACZ,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,QAAQ,EACR,UAAU,EACV,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAEvB,YAAY,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/entitlements/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,aAAa,EACb,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,WAAW,EACX,UAAU,EACV,YAAY,EACZ,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,QAAQ,EACR,UAAU,EACV,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAEvB,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACvG,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9F,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjG,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAE3F,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAClG,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.retentionFloor = exports.requireQuota = exports.requireEntitlement = exports.loadEntitlements = exports.clearEntitlementsCache = exports.invalidateEntitlements = exports.getEntitlements = exports.
|
|
3
|
+
exports.retentionFloor = exports.requireQuota = exports.requireEntitlement = exports.loadEntitlements = exports.clearEntitlementsCache = exports.invalidateEntitlements = exports.getEntitlements = exports.RECONCILED_METERS = exports.runUsageReconciliation = exports.reconcileUsageMeters = exports.setUsage = exports.releaseQuota = exports.consumeQuota = exports.checkQuota = exports.getUsageSnapshot = exports.getUsage = exports.currentPeriod = exports.UsageMeter = exports.fitsWithin = exports.isFeatureKey = exports.isMeterKey = exports.meterPeriod = exports.normalizeLimits = exports.resolveLimits = exports.ENTITLEMENT_CATALOG = exports.DEFAULT_PLAN_LIMITS = exports.FEATURE_DEFINITIONS = exports.METER_DEFINITIONS = exports.FEATURE_KEYS = exports.METER_KEYS = exports.applyLimits = exports.STAFF_PLAN = exports.FALLBACK_PLAN = exports.UNLIMITED = void 0;
|
|
4
4
|
var definitions_1 = require("./definitions");
|
|
5
5
|
Object.defineProperty(exports, "UNLIMITED", { enumerable: true, get: function () { return definitions_1.UNLIMITED; } });
|
|
6
6
|
Object.defineProperty(exports, "FALLBACK_PLAN", { enumerable: true, get: function () { return definitions_1.FALLBACK_PLAN; } });
|
|
@@ -18,9 +18,6 @@ Object.defineProperty(exports, "meterPeriod", { enumerable: true, get: function
|
|
|
18
18
|
Object.defineProperty(exports, "isMeterKey", { enumerable: true, get: function () { return definitions_1.isMeterKey; } });
|
|
19
19
|
Object.defineProperty(exports, "isFeatureKey", { enumerable: true, get: function () { return definitions_1.isFeatureKey; } });
|
|
20
20
|
Object.defineProperty(exports, "fitsWithin", { enumerable: true, get: function () { return definitions_1.fitsWithin; } });
|
|
21
|
-
var mode_1 = require("./mode");
|
|
22
|
-
Object.defineProperty(exports, "getEntitlementsMode", { enumerable: true, get: function () { return mode_1.getEntitlementsMode; } });
|
|
23
|
-
Object.defineProperty(exports, "resetEntitlementsModeWarning", { enumerable: true, get: function () { return mode_1.resetEntitlementsModeWarning; } });
|
|
24
21
|
var UsageMeter_1 = require("./UsageMeter");
|
|
25
22
|
Object.defineProperty(exports, "UsageMeter", { enumerable: true, get: function () { return UsageMeter_1.UsageMeter; } });
|
|
26
23
|
Object.defineProperty(exports, "currentPeriod", { enumerable: true, get: function () { return UsageMeter_1.currentPeriod; } });
|
|
@@ -31,6 +28,10 @@ Object.defineProperty(exports, "checkQuota", { enumerable: true, get: function (
|
|
|
31
28
|
Object.defineProperty(exports, "consumeQuota", { enumerable: true, get: function () { return usage_1.consumeQuota; } });
|
|
32
29
|
Object.defineProperty(exports, "releaseQuota", { enumerable: true, get: function () { return usage_1.releaseQuota; } });
|
|
33
30
|
Object.defineProperty(exports, "setUsage", { enumerable: true, get: function () { return usage_1.setUsage; } });
|
|
31
|
+
var reconcile_1 = require("./reconcile");
|
|
32
|
+
Object.defineProperty(exports, "reconcileUsageMeters", { enumerable: true, get: function () { return reconcile_1.reconcileUsageMeters; } });
|
|
33
|
+
Object.defineProperty(exports, "runUsageReconciliation", { enumerable: true, get: function () { return reconcile_1.runUsageReconciliation; } });
|
|
34
|
+
Object.defineProperty(exports, "RECONCILED_METERS", { enumerable: true, get: function () { return reconcile_1.RECONCILED_METERS; } });
|
|
34
35
|
var client_1 = require("./client");
|
|
35
36
|
Object.defineProperty(exports, "getEntitlements", { enumerable: true, get: function () { return client_1.getEntitlements; } });
|
|
36
37
|
Object.defineProperty(exports, "invalidateEntitlements", { enumerable: true, get: function () { return client_1.invalidateEntitlements; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/entitlements/index.ts"],"names":[],"mappings":";;;AAAA,6CAiBuB;AAhBrB,wGAAA,SAAS,OAAA;AACT,4GAAA,aAAa,OAAA;AACb,yGAAA,UAAU,OAAA;AACV,0GAAA,WAAW,OAAA;AACX,yGAAA,UAAU,OAAA;AACV,2GAAA,YAAY,OAAA;AACZ,gHAAA,iBAAiB,OAAA;AACjB,kHAAA,mBAAmB,OAAA;AACnB,kHAAA,mBAAmB,OAAA;AACnB,kHAAA,mBAAmB,OAAA;AACnB,4GAAA,aAAa,OAAA;AACb,8GAAA,eAAe,OAAA;AACf,0GAAA,WAAW,OAAA;AACX,yGAAA,UAAU,OAAA;AACV,2GAAA,YAAY,OAAA;AACZ,yGAAA,UAAU,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/entitlements/index.ts"],"names":[],"mappings":";;;AAAA,6CAiBuB;AAhBrB,wGAAA,SAAS,OAAA;AACT,4GAAA,aAAa,OAAA;AACb,yGAAA,UAAU,OAAA;AACV,0GAAA,WAAW,OAAA;AACX,yGAAA,UAAU,OAAA;AACV,2GAAA,YAAY,OAAA;AACZ,gHAAA,iBAAiB,OAAA;AACjB,kHAAA,mBAAmB,OAAA;AACnB,kHAAA,mBAAmB,OAAA;AACnB,kHAAA,mBAAmB,OAAA;AACnB,4GAAA,aAAa,OAAA;AACb,8GAAA,eAAe,OAAA;AACf,0GAAA,WAAW,OAAA;AACX,yGAAA,UAAU,OAAA;AACV,2GAAA,YAAY,OAAA;AACZ,yGAAA,UAAU,OAAA;AAcZ,2CAAyD;AAAhD,wGAAA,UAAU,OAAA;AAAE,2GAAA,aAAa,OAAA;AAElC,iCAAuG;AAA9F,iGAAA,QAAQ,OAAA;AAAE,yGAAA,gBAAgB,OAAA;AAAE,mGAAA,UAAU,OAAA;AAAE,qGAAA,YAAY,OAAA;AAAE,qGAAA,YAAY,OAAA;AAAE,iGAAA,QAAQ,OAAA;AAGrF,yCAA8F;AAArF,iHAAA,oBAAoB,OAAA;AAAE,mHAAA,sBAAsB,OAAA;AAAE,8GAAA,iBAAiB,OAAA;AAGxE,mCAA2F;AAAlF,yGAAA,eAAe,OAAA;AAAE,gHAAA,sBAAsB,OAAA;AAAE,gHAAA,sBAAsB,OAAA;AAExE,2CAAkG;AAAzF,8GAAA,gBAAgB,OAAA;AAAE,gHAAA,kBAAkB,OAAA;AAAE,0GAAA,YAAY,OAAA;AAAE,4GAAA,cAAc,OAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/entitlements/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGlD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAa,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/entitlements/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGlD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAa,MAAM,eAAe,CAAC;AAGhE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,uGAAuG;YACvG,YAAY,CAAC,EAAE,YAAY,CAAC;SAC7B;KACF;CACF;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AAEjG,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AAID;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,eAAgC,GAAG,OAAO,CAAC,YAAY,CAAC,CASrH;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,GAAE,kBAAuB,GAAG,cAAc,CAyBxG;AAED,MAAM,WAAW,YAAa,SAAQ,kBAAkB;IACtD;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC,CAAC;CAC9C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,GAAE,YAAiB,GAAG,cAAc,CAkBxF;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAMhG"}
|
|
@@ -12,7 +12,6 @@ const HttpError_1 = require("../errors/HttpError");
|
|
|
12
12
|
const definitions_1 = require("./definitions");
|
|
13
13
|
const client_1 = require("./client");
|
|
14
14
|
const usage_1 = require("./usage");
|
|
15
|
-
const mode_1 = require("./mode");
|
|
16
15
|
const defaultSubject = req => req.user?.email;
|
|
17
16
|
/**
|
|
18
17
|
* Resolves and caches entitlements on the request. Two checks on one route
|
|
@@ -35,23 +34,17 @@ async function loadEntitlements(req, resolve = defaultSubject) {
|
|
|
35
34
|
function requireEntitlement(feature, options = {}) {
|
|
36
35
|
return async (req, _res, next) => {
|
|
37
36
|
try {
|
|
38
|
-
const mode = (0, mode_1.getEntitlementsMode)();
|
|
39
|
-
if (mode === 'off')
|
|
40
|
-
return next();
|
|
41
37
|
const entitlements = await loadEntitlements(req, options.subject);
|
|
42
38
|
if (entitlements.limits.features[feature])
|
|
43
39
|
return next();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
});
|
|
53
|
-
return next();
|
|
54
|
-
}
|
|
40
|
+
logger_1.default.info('entitlement.blocked', {
|
|
41
|
+
event: 'entitlement.blocked',
|
|
42
|
+
feature,
|
|
43
|
+
subjectId: entitlements.email,
|
|
44
|
+
planCode: entitlements.planCode,
|
|
45
|
+
route: req.originalUrl,
|
|
46
|
+
stale: entitlements.stale === true
|
|
47
|
+
});
|
|
55
48
|
throw new HttpError_1.PaymentRequiredError(`Your plan does not include ${feature}`, {
|
|
56
49
|
code: 'FEATURE_NOT_IN_PLAN',
|
|
57
50
|
feature,
|
|
@@ -71,8 +64,6 @@ function requireEntitlement(feature, options = {}) {
|
|
|
71
64
|
function requireQuota(meter, options = {}) {
|
|
72
65
|
return async (req, _res, next) => {
|
|
73
66
|
try {
|
|
74
|
-
if ((0, mode_1.getEntitlementsMode)() === 'off')
|
|
75
|
-
return next();
|
|
76
67
|
const entitlements = await loadEntitlements(req, options.subject);
|
|
77
68
|
const amount = typeof options.amount === 'function' ? options.amount(req) : (options.amount ?? 1);
|
|
78
69
|
await (0, usage_1.checkQuota)({
|
|
@@ -98,8 +89,6 @@ function requireQuota(meter, options = {}) {
|
|
|
98
89
|
* price history it depends on is not.
|
|
99
90
|
*/
|
|
100
91
|
async function retentionFloor(email, now = new Date()) {
|
|
101
|
-
if ((0, mode_1.getEntitlementsMode)() !== 'enforce')
|
|
102
|
-
return null;
|
|
103
92
|
const { limits } = await (0, client_1.getEntitlements)(email);
|
|
104
93
|
const days = limits.retentionDays;
|
|
105
94
|
if (days === definitions_1.UNLIMITED || days < 0)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/entitlements/middleware.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/entitlements/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { MeterKey } from './definitions';
|
|
2
|
+
/**
|
|
3
|
+
* Rebuild the cumulative usage meters from the data they describe.
|
|
4
|
+
*
|
|
5
|
+
* **Why this can exist at all:** every meter reconciled here is *derived*.
|
|
6
|
+
* `storageBytes` is the sum of photo sizes, `trackedTickers` the count of
|
|
7
|
+
* active symbols, `seats` the members of the groups a subject owns — in each
|
|
8
|
+
* case the underlying collection is the record and the counter is only a cache
|
|
9
|
+
* of it, kept hot because a quota check cannot afford the aggregation. So the
|
|
10
|
+
* counter can always be recomputed, and reconciliation is cache invalidation.
|
|
11
|
+
*
|
|
12
|
+
* `aiJobs` is deliberately absent, and its absence is the same rule read the
|
|
13
|
+
* other way: nothing records that a GPU job happened except the counter itself,
|
|
14
|
+
* so there is no truth to recompute it from. That is precisely why
|
|
15
|
+
* `consumeQuota` rolls its increment back on rejection rather than leaving it —
|
|
16
|
+
* a leaked increment on that meter is permanent, where here it is temporary.
|
|
17
|
+
*
|
|
18
|
+
* **On racing with live writes:** a `consumeQuota` landing between the
|
|
19
|
+
* aggregation and the write is lost. That is tolerable for exactly the reason
|
|
20
|
+
* above — the next run re-derives the same number from source and converges —
|
|
21
|
+
* so this is scheduled at a quiet hour rather than defended with a lock.
|
|
22
|
+
*
|
|
23
|
+
* Reads collections owned by other services through the raw driver rather than
|
|
24
|
+
* mongoose models. They are not this library's documents to model, every
|
|
25
|
+
* service shares one database, and going through the driver avoids registering
|
|
26
|
+
* a second model for a schema that already exists elsewhere.
|
|
27
|
+
*/
|
|
28
|
+
/** Meters whose value can be recomputed from source data. See the note above. */
|
|
29
|
+
export declare const RECONCILED_METERS: readonly MeterKey[];
|
|
30
|
+
export interface ReconcileOptions {
|
|
31
|
+
/** Write the corrections. Default false — the caller must opt in to mutating. */
|
|
32
|
+
apply?: boolean;
|
|
33
|
+
/** Restrict to a single subject, for a targeted repair. */
|
|
34
|
+
subject?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface MeterChange {
|
|
37
|
+
subjectId: string;
|
|
38
|
+
meter: MeterKey;
|
|
39
|
+
/** `null` when no counter existed yet. */
|
|
40
|
+
from: number | null;
|
|
41
|
+
to: number;
|
|
42
|
+
}
|
|
43
|
+
export interface OrphanedMeter {
|
|
44
|
+
subjectId: string;
|
|
45
|
+
meter: string;
|
|
46
|
+
count: number;
|
|
47
|
+
}
|
|
48
|
+
export interface ReconcileReport {
|
|
49
|
+
applied: boolean;
|
|
50
|
+
scannedUsers: number;
|
|
51
|
+
/** Subjects with any attributable usage, before any `subject` filter. */
|
|
52
|
+
subjects: number;
|
|
53
|
+
expected: Record<string, Record<MeterKey, number>>;
|
|
54
|
+
changes: MeterChange[];
|
|
55
|
+
written: number;
|
|
56
|
+
orphanedMeters: OrphanedMeter[];
|
|
57
|
+
warnings: string[];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Recompute every derived meter and, with `apply`, write back the ones that
|
|
61
|
+
* disagree.
|
|
62
|
+
*
|
|
63
|
+
* Idempotent: it computes absolute values and writes only differences, so a
|
|
64
|
+
* second run in a row is a no-op.
|
|
65
|
+
*/
|
|
66
|
+
export declare function reconcileUsageMeters(options?: ReconcileOptions): Promise<ReconcileReport>;
|
|
67
|
+
/**
|
|
68
|
+
* Run a reconciliation and log the outcome. The scheduled entry point.
|
|
69
|
+
*
|
|
70
|
+
* Never throws: this runs from a cron callback with no caller to catch it, and
|
|
71
|
+
* an unhandled rejection there takes the process down over a housekeeping job.
|
|
72
|
+
*/
|
|
73
|
+
export declare function runUsageReconciliation(): Promise<ReconcileReport | null>;
|
|
74
|
+
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/entitlements/reconcile.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,EAAE,SAAS,QAAQ,EAAgD,CAAC;AAElG,MAAM,WAAW,gBAAgB;IAC/B,iFAAiF;IACjF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA2LD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CAkFnG;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAiC9E"}
|