@tumbaland/backend-core 1.27.0 → 1.29.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.
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/entitlements/definitions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,QAAQ,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAE9E;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,aAAa,CAAC;AAEvC,8FAA8F;AAC9F,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,CAAC;AAE1C,2FAA2F;AAC3F,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,eAAe,EA6B9C,CAAC;AAEX,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAMlD,CAAC;AAEX,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAAsC,CAAC;AACjF,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAAwC,CAAC;AAMvF,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,WAAW,CAExD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,CAE3D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D;AAED,MAAM,WAAW,UAAU;IACzB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACtC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAID;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CA+BpE,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,eAAO,MAAM,aAAa,SAAS,CAAC;AAEpC;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAG3D;AAiBD;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,UAAU,CAc1F;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,CAAC,EAAE,iBAAiB,GAAG,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAE/F;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,OAAO,CAG3E;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;CAa/B,CAAC"}
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/entitlements/definitions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,QAAQ,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAE9E;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,aAAa,CAAC;AAEvC,8FAA8F;AAC9F,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,CAAC;AAE1C,2FAA2F;AAC3F,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,iBAAiB,EAAE,SAAS,eAAe,EA6B9C,CAAC;AAEX,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAMlD,CAAC;AAEX,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAAsC,CAAC;AACjF,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAAwC,CAAC;AAMvF,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,WAAW,CAExD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,CAE3D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D;AAED,MAAM,WAAW,UAAU;IACzB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACtC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAID;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAsCpE,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,eAAO,MAAM,aAAa,SAAS,CAAC;AAEpC;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAG3D;AAiBD;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,UAAU,CAc1F;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,CAAC,EAAE,iBAAiB,GAAG,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAE/F;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,OAAO,CAG3E;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;CAa/B,CAAC"}
@@ -94,13 +94,20 @@ exports.DEFAULT_PLAN_LIMITS = {
94
94
  features: { cleanExport: false },
95
95
  retentionDays: 90
96
96
  },
97
- personal: {
98
- meters: { storageBytes: 50 * GB, aiJobs: 200, trackedTickers: 25, seats: 1 },
99
- features: { cleanExport: true },
100
- retentionDays: exports.UNLIMITED
101
- },
102
- family: {
103
- meters: { storageBytes: 250 * GB, aiJobs: 600, trackedTickers: 100, seats: 5 },
97
+ /**
98
+ * The single paid plan, and it is a *support* tier rather than a product
99
+ * edition. `personal` and `family` were consolidated into it: two paid tiers
100
+ * imply a business deciding what to withhold from which customer, which is
101
+ * the wrong shape for a project asking people to chip in. One tier that
102
+ * grants everything removes that question entirely.
103
+ *
104
+ * Limits are the old `family` numbers — pooled seats, unlimited retention —
105
+ * except storage, which is 50 GB rather than 250. 250 GB was priced for a
106
+ * household paying €12; at €5 it is more object storage than the support is
107
+ * covering.
108
+ */
109
+ support: {
110
+ meters: { storageBytes: 50 * GB, aiJobs: 600, trackedTickers: 100, seats: 5 },
104
111
  features: { cleanExport: true },
105
112
  retentionDays: exports.UNLIMITED
106
113
  },
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/entitlements/definitions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/entitlements/definitions.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ export type { MeterKey, FeatureKey, MeterPeriod, MeterDefinition, FeatureDefinit
3
3
  export type { Entitlements } from './types';
4
4
  export { UsageMeter, currentPeriod } from './UsageMeter';
5
5
  export type { IUsageMeter } from './UsageMeter';
6
- export { getUsage, getUsageSnapshot, checkQuota, consumeQuota, releaseQuota, setUsage } from './usage';
6
+ export { getUsage, getUsageSnapshot, checkQuota, consumeQuota, recordUsage, releaseQuota, setUsage } from './usage';
7
7
  export type { QuotaRequest } from './usage';
8
8
  export { reconcileUsageMeters, runUsageReconciliation, RECONCILED_METERS } from './reconcile';
9
9
  export type { ReconcileOptions, ReconcileReport, MeterChange, OrphanedMeter } from './reconcile';
@@ -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,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
+ {"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,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACpH,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.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;
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.recordUsage = 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; } });
@@ -26,6 +26,7 @@ Object.defineProperty(exports, "getUsage", { enumerable: true, get: function ()
26
26
  Object.defineProperty(exports, "getUsageSnapshot", { enumerable: true, get: function () { return usage_1.getUsageSnapshot; } });
27
27
  Object.defineProperty(exports, "checkQuota", { enumerable: true, get: function () { return usage_1.checkQuota; } });
28
28
  Object.defineProperty(exports, "consumeQuota", { enumerable: true, get: function () { return usage_1.consumeQuota; } });
29
+ Object.defineProperty(exports, "recordUsage", { enumerable: true, get: function () { return usage_1.recordUsage; } });
29
30
  Object.defineProperty(exports, "releaseQuota", { enumerable: true, get: function () { return usage_1.releaseQuota; } });
30
31
  Object.defineProperty(exports, "setUsage", { enumerable: true, get: function () { return usage_1.setUsage; } });
31
32
  var reconcile_1 = require("./reconcile");
@@ -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;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
+ {"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,iCAAoH;AAA3G,iGAAA,QAAQ,OAAA;AAAE,yGAAA,gBAAgB,OAAA;AAAE,mGAAA,UAAU,OAAA;AAAE,qGAAA,YAAY,OAAA;AAAE,oGAAA,WAAW,OAAA;AAAE,qGAAA,YAAY,OAAA;AAAE,iGAAA,QAAQ,OAAA;AAGlG,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"}
@@ -53,4 +53,25 @@ export declare function releaseQuota(subjectId: string, meter: MeterKey, amount?
53
53
  * for periodic reconciliation against the source of truth.
54
54
  */
55
55
  export declare function setUsage(subjectId: string, meter: MeterKey, count: number, now?: Date): Promise<void>;
56
+ /**
57
+ * Record usage that has already happened, without a limit check.
58
+ *
59
+ * The storage gate runs on the *signed-URL request* — the only point where a
60
+ * file's size is known before its bytes are written — so by the time the photo
61
+ * record is created the object is already in MinIO. Neither of the existing
62
+ * primitives fits that moment: `checkQuota` records nothing, and `consumeQuota`
63
+ * would reject and roll back, leaving the meter understating bytes that are
64
+ * genuinely stored while the user is refused a record for storage they have
65
+ * already paid for.
66
+ *
67
+ * So this increments past the limit deliberately. A cumulative meter can only
68
+ * be pushed over by a race or a stale entitlement, both bounded by one upload,
69
+ * and the gate then refuses the *next* one — which is the correct behaviour:
70
+ * the ceiling stops new writes, it does not un-store bytes.
71
+ *
72
+ * Never throws, for the same reason `releaseQuota` does not: the resource
73
+ * exists, the caller is on its success path, and a failed counter update is
74
+ * repaired by the nightly reconciliation this meter is derived for.
75
+ */
76
+ export declare function recordUsage(subjectId: string, meter: MeterKey, amount?: number, now?: Date): Promise<void>;
56
77
  //# sourceMappingURL=usage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/entitlements/usage.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAqC,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,YAAY,CAAC;IAC3B,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,sDAAsD;AACtD,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAO9F;AAED,sFAAsF;AACtF,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAYvG;AAiCD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CASjE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBnE;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,QAAQ,EACf,MAAM,SAAI,EACV,GAAG,CAAC,EAAE,IAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,IAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAMf"}
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/entitlements/usage.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAqC,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,YAAY,CAAC;IAC3B,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,sDAAsD;AACtD,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAO9F;AAED,sFAAsF;AACtF,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAYvG;AAiCD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CASjE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBnE;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,QAAQ,EACf,MAAM,SAAI,EACV,GAAG,CAAC,EAAE,IAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,IAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,QAAQ,EACf,MAAM,SAAI,EACV,GAAG,CAAC,EAAE,IAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAiBf"}
@@ -9,6 +9,7 @@ exports.checkQuota = checkQuota;
9
9
  exports.consumeQuota = consumeQuota;
10
10
  exports.releaseQuota = releaseQuota;
11
11
  exports.setUsage = setUsage;
12
+ exports.recordUsage = recordUsage;
12
13
  const logger_1 = __importDefault(require("../logging/logger"));
13
14
  const HttpError_1 = require("../errors/HttpError");
14
15
  const UsageMeter_1 = require("./UsageMeter");
@@ -147,4 +148,39 @@ async function releaseQuota(subjectId, meter, amount = 1, now) {
147
148
  async function setUsage(subjectId, meter, count, now) {
148
149
  await UsageMeter_1.UsageMeter.updateOne({ subjectId: subjectId.toLowerCase(), meter, period: (0, UsageMeter_1.currentPeriod)(meter, now) }, { $set: { count: Math.max(0, Math.floor(count)) } }, { upsert: true, setDefaultsOnInsert: true });
149
150
  }
151
+ /**
152
+ * Record usage that has already happened, without a limit check.
153
+ *
154
+ * The storage gate runs on the *signed-URL request* — the only point where a
155
+ * file's size is known before its bytes are written — so by the time the photo
156
+ * record is created the object is already in MinIO. Neither of the existing
157
+ * primitives fits that moment: `checkQuota` records nothing, and `consumeQuota`
158
+ * would reject and roll back, leaving the meter understating bytes that are
159
+ * genuinely stored while the user is refused a record for storage they have
160
+ * already paid for.
161
+ *
162
+ * So this increments past the limit deliberately. A cumulative meter can only
163
+ * be pushed over by a race or a stale entitlement, both bounded by one upload,
164
+ * and the gate then refuses the *next* one — which is the correct behaviour:
165
+ * the ceiling stops new writes, it does not un-store bytes.
166
+ *
167
+ * Never throws, for the same reason `releaseQuota` does not: the resource
168
+ * exists, the caller is on its success path, and a failed counter update is
169
+ * repaired by the nightly reconciliation this meter is derived for.
170
+ */
171
+ async function recordUsage(subjectId, meter, amount = 1, now) {
172
+ if (amount <= 0)
173
+ return;
174
+ try {
175
+ await UsageMeter_1.UsageMeter.updateOne({ subjectId: subjectId.toLowerCase(), meter, period: (0, UsageMeter_1.currentPeriod)(meter, now) }, { $inc: { count: amount } }, { upsert: true, setDefaultsOnInsert: true });
176
+ }
177
+ catch (err) {
178
+ logger_1.default.warn('Failed to record usage', {
179
+ subjectId,
180
+ meter,
181
+ amount,
182
+ error: err?.message
183
+ });
184
+ }
185
+ }
150
186
  //# sourceMappingURL=usage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/entitlements/usage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/entitlements/usage.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@ export { HttpError, BadRequestError, UnauthorizedError, ForbiddenError, NotFound
12
12
  export type { PaymentRequiredDetails } from './errors/HttpError';
13
13
  export * from './entitlements';
14
14
  export * from './groups';
15
- export { authenticateToken } from './middleware/authMiddleware';
15
+ export { authenticateToken, optionalAuth } from './middleware/authMiddleware';
16
16
  export { requireInternalServiceToken, internalServiceTokenBypass, allowUserOrInternalService, verifyInternalToken, INTERNAL_TOKEN_HEADER, SERVICE_ID_HEADER } from './middleware/internalServiceAuth';
17
17
  export { createCorsMiddleware } from './middleware/corsMiddleware';
18
18
  export { errorHandler } from './middleware/errorHandler';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAG9D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC7K,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAGjE,cAAc,gBAAgB,CAAC;AAG/B,cAAc,UAAU,CAAC;AAGzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,0BAA0B,EAC1B,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAC1G,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACnH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7E,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG9E,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EACf,QAAQ,EACT,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,UAAU,EACV,SAAS,EACT,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,SAAS,EACT,UAAU,EACV,aAAa,EACb,kBAAkB,EACnB,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAG9D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC7K,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAGjE,cAAc,gBAAgB,CAAC;AAG/B,cAAc,UAAU,CAAC;AAGzB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,0BAA0B,EAC1B,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAC1G,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACnH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7E,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG9E,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EACf,QAAQ,EACT,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,UAAU,EACV,SAAS,EACT,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,SAAS,EACT,UAAU,EACV,aAAa,EACb,kBAAkB,EACnB,MAAM,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -18,8 +18,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
18
18
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.initTracer = exports.register = exports.businessMetrics = exports.databaseQueriesTotal = exports.databaseQueryDuration = exports.httpRequestsTotal = exports.httpRequestDuration = exports.metricsMiddleware = exports.canAccessResource = exports.buildAccessQuery = exports.sendError = exports.sendSuccess = exports.createApiResponse = exports.correlationMiddleware = exports.generateCorrelationId = exports.validate = exports.strictRateLimiter = exports.standardRateLimiter = exports.createRateLimiter = exports.securityHeaders = exports.simpleRequestLogger = exports.requestLoggerWithMetrics = exports.requestLogger = exports.errorHandler = exports.createCorsMiddleware = exports.SERVICE_ID_HEADER = exports.INTERNAL_TOKEN_HEADER = exports.verifyInternalToken = exports.allowUserOrInternalService = exports.internalServiceTokenBypass = exports.requireInternalServiceToken = exports.authenticateToken = exports.PaymentRequiredError = exports.TooManyRequestsError = exports.ConflictError = exports.NotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.BadRequestError = exports.HttpError = exports.requireEnv = exports.disconnectDB = exports.connectDB = exports.createHealthCheck = exports.metricsHandler = exports.healthLive = exports.healthCheck = exports.logger = exports.registerShutdown = exports.createBaseApp = void 0;
22
- exports.extractSpanContext = exports.injectHeaders = exports.setSpanTag = exports.logToSpan = exports.createChildSpan = exports.tracingMiddleware = exports.startSpan = exports.getTracer = void 0;
21
+ exports.register = exports.businessMetrics = exports.databaseQueriesTotal = exports.databaseQueryDuration = exports.httpRequestsTotal = exports.httpRequestDuration = exports.metricsMiddleware = exports.canAccessResource = exports.buildAccessQuery = exports.sendError = exports.sendSuccess = exports.createApiResponse = exports.correlationMiddleware = exports.generateCorrelationId = exports.validate = exports.strictRateLimiter = exports.standardRateLimiter = exports.createRateLimiter = exports.securityHeaders = exports.simpleRequestLogger = exports.requestLoggerWithMetrics = exports.requestLogger = exports.errorHandler = exports.createCorsMiddleware = exports.SERVICE_ID_HEADER = exports.INTERNAL_TOKEN_HEADER = exports.verifyInternalToken = exports.allowUserOrInternalService = exports.internalServiceTokenBypass = exports.requireInternalServiceToken = exports.optionalAuth = exports.authenticateToken = exports.PaymentRequiredError = exports.TooManyRequestsError = exports.ConflictError = exports.NotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.BadRequestError = exports.HttpError = exports.requireEnv = exports.disconnectDB = exports.connectDB = exports.createHealthCheck = exports.metricsHandler = exports.healthLive = exports.healthCheck = exports.logger = exports.registerShutdown = exports.createBaseApp = void 0;
22
+ exports.extractSpanContext = exports.injectHeaders = exports.setSpanTag = exports.logToSpan = exports.createChildSpan = exports.tracingMiddleware = exports.startSpan = exports.getTracer = exports.initTracer = void 0;
23
23
  // App bootstrap
24
24
  var createBaseApp_1 = require("./app/createBaseApp");
25
25
  Object.defineProperty(exports, "createBaseApp", { enumerable: true, get: function () { return createBaseApp_1.createBaseApp; } });
@@ -59,6 +59,7 @@ __exportStar(require("./groups"), exports);
59
59
  // Middleware
60
60
  var authMiddleware_1 = require("./middleware/authMiddleware");
61
61
  Object.defineProperty(exports, "authenticateToken", { enumerable: true, get: function () { return authMiddleware_1.authenticateToken; } });
62
+ Object.defineProperty(exports, "optionalAuth", { enumerable: true, get: function () { return authMiddleware_1.optionalAuth; } });
62
63
  var internalServiceAuth_1 = require("./middleware/internalServiceAuth");
63
64
  Object.defineProperty(exports, "requireInternalServiceToken", { enumerable: true, get: function () { return internalServiceAuth_1.requireInternalServiceToken; } });
64
65
  Object.defineProperty(exports, "internalServiceTokenBypass", { enumerable: true, get: function () { return internalServiceAuth_1.internalServiceTokenBypass; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,qFAAqF;;;;;;;;;;;;;;;;;;;;;AAErF,gBAAgB;AAChB,qDAAoD;AAA3C,8GAAA,aAAa,OAAA;AAEtB,2CAAkD;AAAzC,4GAAA,gBAAgB,OAAA;AAGzB,UAAU;AACV,2CAAqD;AAA5C,iHAAA,OAAO,OAAU;AAE1B,gBAAgB;AAChB,8DAAoF;AAA3E,+GAAA,WAAW,OAAA;AAAE,8GAAA,UAAU,OAAA;AAAE,kHAAA,cAAc,OAAA;AAChD,gEAA+D;AAAtD,sHAAA,iBAAiB,OAAA;AAG1B,WAAW;AACX,oDAAgE;AAAvD,uGAAA,SAAS,OAAA;AAAE,0GAAA,YAAY,OAAA;AAEhC,SAAS;AACT,oCAA0C;AAAjC,iGAAA,UAAU,OAAA;AAEnB,SAAS;AACT,gDAA6K;AAApK,sGAAA,SAAS,OAAA;AAAE,4GAAA,eAAe,OAAA;AAAE,8GAAA,iBAAiB,OAAA;AAAE,2GAAA,cAAc,OAAA;AAAE,0GAAA,aAAa,OAAA;AAAE,0GAAA,aAAa,OAAA;AAAE,iHAAA,oBAAoB,OAAA;AAAE,iHAAA,oBAAoB,OAAA;AAGhJ,wDAAwD;AACxD,iDAA+B;AAE/B,0DAA0D;AAC1D,2CAAyB;AAEzB,aAAa;AACb,8DAAgE;AAAvD,mHAAA,iBAAiB,OAAA;AAC1B,wEAO0C;AANxC,kIAAA,2BAA2B,OAAA;AAC3B,iIAAA,0BAA0B,OAAA;AAC1B,iIAAA,0BAA0B,OAAA;AAC1B,0HAAA,mBAAmB,OAAA;AACnB,4HAAA,qBAAqB,OAAA;AACrB,wHAAA,iBAAiB,OAAA;AAEnB,8DAAmE;AAA1D,sHAAA,oBAAoB,OAAA;AAC7B,0DAAyD;AAAhD,4GAAA,YAAY,OAAA;AACrB,4DAA0G;AAAjG,8GAAA,aAAa,OAAA;AAAE,yHAAA,wBAAwB,OAAA;AAAE,oHAAA,mBAAmB,OAAA;AACrE,kDAAmH;AAA1G,2GAAA,eAAe,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AAAE,+GAAA,mBAAmB,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AACnF,kDAAiD;AAAxC,oGAAA,QAAQ,OAAA;AAKjB,QAAQ;AACR,mDAAmF;AAA1E,oHAAA,qBAAqB,OAAA;AAAE,oHAAA,qBAAqB,OAAA;AACrD,6CAA6E;AAApE,6GAAA,iBAAiB,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,qGAAA,SAAS,OAAA;AAElD,2DAA8E;AAArE,mHAAA,gBAAgB,OAAA;AAAE,oHAAA,iBAAiB,OAAA;AAE5C,UAAU;AACV,qCAQmB;AAPjB,4GAAA,iBAAiB,OAAA;AACjB,8GAAA,mBAAmB,OAAA;AACnB,4GAAA,iBAAiB,OAAA;AACjB,gHAAA,qBAAqB,OAAA;AACrB,+GAAA,oBAAoB,OAAA;AACpB,0GAAA,eAAe,OAAA;AACf,mGAAA,QAAQ,OAAA;AAGV,UAAU;AACV,qCAUmB;AATjB,qGAAA,UAAU,OAAA;AACV,oGAAA,SAAS,OAAA;AACT,oGAAA,SAAS,OAAA;AACT,4GAAA,iBAAiB,OAAA;AACjB,0GAAA,eAAe,OAAA;AACf,oGAAA,SAAS,OAAA;AACT,qGAAA,UAAU,OAAA;AACV,wGAAA,aAAa,OAAA;AACb,6GAAA,kBAAkB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,qFAAqF;;;;;;;;;;;;;;;;;;;;;AAErF,gBAAgB;AAChB,qDAAoD;AAA3C,8GAAA,aAAa,OAAA;AAEtB,2CAAkD;AAAzC,4GAAA,gBAAgB,OAAA;AAGzB,UAAU;AACV,2CAAqD;AAA5C,iHAAA,OAAO,OAAU;AAE1B,gBAAgB;AAChB,8DAAoF;AAA3E,+GAAA,WAAW,OAAA;AAAE,8GAAA,UAAU,OAAA;AAAE,kHAAA,cAAc,OAAA;AAChD,gEAA+D;AAAtD,sHAAA,iBAAiB,OAAA;AAG1B,WAAW;AACX,oDAAgE;AAAvD,uGAAA,SAAS,OAAA;AAAE,0GAAA,YAAY,OAAA;AAEhC,SAAS;AACT,oCAA0C;AAAjC,iGAAA,UAAU,OAAA;AAEnB,SAAS;AACT,gDAA6K;AAApK,sGAAA,SAAS,OAAA;AAAE,4GAAA,eAAe,OAAA;AAAE,8GAAA,iBAAiB,OAAA;AAAE,2GAAA,cAAc,OAAA;AAAE,0GAAA,aAAa,OAAA;AAAE,0GAAA,aAAa,OAAA;AAAE,iHAAA,oBAAoB,OAAA;AAAE,iHAAA,oBAAoB,OAAA;AAGhJ,wDAAwD;AACxD,iDAA+B;AAE/B,0DAA0D;AAC1D,2CAAyB;AAEzB,aAAa;AACb,8DAA8E;AAArE,mHAAA,iBAAiB,OAAA;AAAE,8GAAA,YAAY,OAAA;AACxC,wEAO0C;AANxC,kIAAA,2BAA2B,OAAA;AAC3B,iIAAA,0BAA0B,OAAA;AAC1B,iIAAA,0BAA0B,OAAA;AAC1B,0HAAA,mBAAmB,OAAA;AACnB,4HAAA,qBAAqB,OAAA;AACrB,wHAAA,iBAAiB,OAAA;AAEnB,8DAAmE;AAA1D,sHAAA,oBAAoB,OAAA;AAC7B,0DAAyD;AAAhD,4GAAA,YAAY,OAAA;AACrB,4DAA0G;AAAjG,8GAAA,aAAa,OAAA;AAAE,yHAAA,wBAAwB,OAAA;AAAE,oHAAA,mBAAmB,OAAA;AACrE,kDAAmH;AAA1G,2GAAA,eAAe,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AAAE,+GAAA,mBAAmB,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AACnF,kDAAiD;AAAxC,oGAAA,QAAQ,OAAA;AAKjB,QAAQ;AACR,mDAAmF;AAA1E,oHAAA,qBAAqB,OAAA;AAAE,oHAAA,qBAAqB,OAAA;AACrD,6CAA6E;AAApE,6GAAA,iBAAiB,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,qGAAA,SAAS,OAAA;AAElD,2DAA8E;AAArE,mHAAA,gBAAgB,OAAA;AAAE,oHAAA,iBAAiB,OAAA;AAE5C,UAAU;AACV,qCAQmB;AAPjB,4GAAA,iBAAiB,OAAA;AACjB,8GAAA,mBAAmB,OAAA;AACnB,4GAAA,iBAAiB,OAAA;AACjB,gHAAA,qBAAqB,OAAA;AACrB,+GAAA,oBAAoB,OAAA;AACpB,0GAAA,eAAe,OAAA;AACf,mGAAA,QAAQ,OAAA;AAGV,UAAU;AACV,qCAUmB;AATjB,qGAAA,UAAU,OAAA;AACV,oGAAA,SAAS,OAAA;AACT,oGAAA,SAAS,OAAA;AACT,4GAAA,iBAAiB,OAAA;AACjB,0GAAA,eAAe,OAAA;AACf,oGAAA,SAAS,OAAA;AACT,qGAAA,UAAU,OAAA;AACV,wGAAA,aAAa,OAAA;AACb,6GAAA,kBAAkB,OAAA"}
@@ -6,4 +6,18 @@ import { Request, Response, NextFunction } from 'express';
6
6
  * `../types/auth`, no `(req as any)` cast needed.
7
7
  */
8
8
  export declare function authenticateToken(req: Request, res: Response, next: NextFunction): void;
9
+ /**
10
+ * Attaches the user when a valid token is present, and never rejects.
11
+ *
12
+ * For routes whose response is public but *richer* when signed in — the plan
13
+ * catalog is the case this exists for: a signed-out visitor on the landing
14
+ * page must be able to read prices, while a signed-in one additionally gets
15
+ * the hidden plan they are actually on.
16
+ *
17
+ * An invalid or expired token is treated as no token rather than as a 401. The
18
+ * caller asked for public data; refusing it because a stale cookie is lying
19
+ * around would break the landing page for exactly the people most likely to
20
+ * have one.
21
+ */
22
+ export declare function optionalAuth(req: Request, _res: Response, next: NextFunction): void;
9
23
  //# sourceMappingURL=authMiddleware.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"authMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware/authMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAK1D;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAiBvF"}
1
+ {"version":3,"file":"authMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware/authMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAK1D;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAiBvF;AACD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAcnF"}
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.authenticateToken = authenticateToken;
7
+ exports.optionalAuth = optionalAuth;
7
8
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
8
9
  const env_1 = require("../config/env");
9
10
  /**
@@ -29,4 +30,32 @@ function authenticateToken(req, res, next) {
29
30
  res.status(401).json({ success: false, message: 'Invalid or expired token' });
30
31
  }
31
32
  }
33
+ /**
34
+ * Attaches the user when a valid token is present, and never rejects.
35
+ *
36
+ * For routes whose response is public but *richer* when signed in — the plan
37
+ * catalog is the case this exists for: a signed-out visitor on the landing
38
+ * page must be able to read prices, while a signed-in one additionally gets
39
+ * the hidden plan they are actually on.
40
+ *
41
+ * An invalid or expired token is treated as no token rather than as a 401. The
42
+ * caller asked for public data; refusing it because a stale cookie is lying
43
+ * around would break the landing page for exactly the people most likely to
44
+ * have one.
45
+ */
46
+ function optionalAuth(req, _res, next) {
47
+ try {
48
+ const JWT_SECRET = (0, env_1.requireEnv)('JWT_SECRET');
49
+ const token = req.cookies?.access_token || req.headers.authorization?.replace('Bearer ', '');
50
+ if (token) {
51
+ const user = jsonwebtoken_1.default.verify(token, JWT_SECRET);
52
+ req.user = user;
53
+ req.userGroups = user.groups ?? [];
54
+ }
55
+ }
56
+ catch {
57
+ // Deliberately ignored — see above.
58
+ }
59
+ next();
60
+ }
32
61
  //# sourceMappingURL=authMiddleware.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"authMiddleware.js","sourceRoot":"","sources":["../../src/middleware/authMiddleware.ts"],"names":[],"mappings":";;;;;AAWA,8CAiBC;AA3BD,gEAA+B;AAC/B,uCAA2C;AAG3C;;;;;GAKG;AACH,SAAgB,iBAAiB,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IAC/E,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAA,gBAAU,EAAC,YAAY,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,YAAY,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAE7F,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAC;YAC3E,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAgB,CAAC;QAC1D,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAChB,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;QACnC,IAAI,EAAE,CAAC;IACT,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC,CAAC;IAChF,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"authMiddleware.js","sourceRoot":"","sources":["../../src/middleware/authMiddleware.ts"],"names":[],"mappings":";;;;;AAWA,8CAiBC;AAcD,oCAcC;AAvDD,gEAA+B;AAC/B,uCAA2C;AAG3C;;;;;GAKG;AACH,SAAgB,iBAAiB,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IAC/E,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAA,gBAAU,EAAC,YAAY,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,YAAY,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAE7F,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAC;YAC3E,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAgB,CAAC;QAC1D,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAChB,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;QACnC,IAAI,EAAE,CAAC;IACT,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AACD;;;;;;;;;;;;GAYG;AACH,SAAgB,YAAY,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB;IAC3E,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAA,gBAAU,EAAC,YAAY,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,YAAY,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAE7F,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,IAAI,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAgB,CAAC;YAC1D,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;YAChB,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;QACrC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,oCAAoC;IACtC,CAAC;IACD,IAAI,EAAE,CAAC;AACT,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tumbaland/backend-core",
3
- "version": "1.27.0",
3
+ "version": "1.29.0",
4
4
  "description": "Core shared functionality for Tumbaland backend services",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,10 +17,10 @@ function jsonResponse(body: unknown, status = 200): Response {
17
17
  function paidResponse() {
18
18
  return jsonResponse({
19
19
  success: true,
20
- planCode: 'personal',
20
+ planCode: 'support',
21
21
  status: 'active',
22
22
  limits: { meters: { storageBytes: 12345 } },
23
- upgradeTo: 'family'
23
+ upgradeTo: 'support'
24
24
  });
25
25
  }
26
26
 
@@ -53,12 +53,12 @@ describe('getEntitlements', () => {
53
53
  headers: expect.objectContaining({ 'x-internal-token': 'test-internal-token', 'x-service-id': 'album-service' })
54
54
  })
55
55
  );
56
- expect(result.planCode).toBe('personal');
57
- expect(result.upgradeTo).toBe('family');
56
+ expect(result.planCode).toBe('support');
57
+ expect(result.upgradeTo).toBe('support');
58
58
  expect(result.limits.meters.storageBytes).toBe(12345);
59
59
  // Keys the payload omitted are filled from that plan's defaults rather than
60
60
  // arriving undefined and comparing as NaN inside a quota check.
61
- expect(result.limits.meters.seats).toBe(DEFAULT_PLAN_LIMITS.personal.meters.seats);
61
+ expect(result.limits.meters.seats).toBe(DEFAULT_PLAN_LIMITS.support.meters.seats);
62
62
  expect(result.stale).toBeUndefined();
63
63
  });
64
64
 
@@ -135,7 +135,7 @@ describe('when payment-service cannot be reached', () => {
135
135
  const recovered = await getEntitlements('user@example.com');
136
136
 
137
137
  expect(failed.stale).toBe(true);
138
- expect(recovered.planCode).toBe('personal');
138
+ expect(recovered.planCode).toBe('support');
139
139
  });
140
140
 
141
141
  it('fails closed without calling out at all when the channel is unconfigured', async () => {
@@ -16,7 +16,7 @@ import {
16
16
 
17
17
  describe('resolveLimits', () => {
18
18
  it('returns the defaults for a known plan code', () => {
19
- expect(resolveLimits('personal')).toEqual(DEFAULT_PLAN_LIMITS.personal);
19
+ expect(resolveLimits('support')).toEqual(DEFAULT_PLAN_LIMITS.support);
20
20
  });
21
21
 
22
22
  it('falls back to free for an unknown code, never to unlimited', () => {
@@ -36,12 +36,12 @@ describe('resolveLimits', () => {
36
36
 
37
37
  describe('normalizeLimits', () => {
38
38
  it('applies stored values over the plan defaults', () => {
39
- const limits = normalizeLimits({ meters: { storageBytes: 123 }, retentionDays: 30 }, 'personal');
39
+ const limits = normalizeLimits({ meters: { storageBytes: 123 }, retentionDays: 30 }, 'support');
40
40
 
41
41
  expect(limits.meters.storageBytes).toBe(123);
42
42
  expect(limits.retentionDays).toBe(30);
43
43
  // Untouched keys keep the plan's defaults rather than resetting to free.
44
- expect(limits.meters.trackedTickers).toBe(DEFAULT_PLAN_LIMITS.personal.meters.trackedTickers);
44
+ expect(limits.meters.trackedTickers).toBe(DEFAULT_PLAN_LIMITS.support.meters.trackedTickers);
45
45
  });
46
46
 
47
47
  it('drops keys that no code enforces', () => {
@@ -66,17 +66,17 @@ describe('normalizeLimits', () => {
66
66
  it('ignores non-numeric and non-boolean junk instead of producing NaN limits', () => {
67
67
  const limits = normalizeLimits(
68
68
  { meters: { seats: 'lots' as unknown as number }, features: { cleanExport: 'yes' as unknown as boolean } },
69
- 'family'
69
+ 'support'
70
70
  );
71
71
 
72
- expect(limits.meters.seats).toBe(DEFAULT_PLAN_LIMITS.family.meters.seats);
72
+ expect(limits.meters.seats).toBe(DEFAULT_PLAN_LIMITS.support.meters.seats);
73
73
  expect(limits.features.cleanExport).toBe(true);
74
74
  });
75
75
 
76
76
  it('returns the plan defaults for an empty or missing payload', () => {
77
- expect(normalizeLimits(undefined, 'family')).toEqual(DEFAULT_PLAN_LIMITS.family);
78
- expect(normalizeLimits(null, 'family')).toEqual(DEFAULT_PLAN_LIMITS.family);
79
- expect(normalizeLimits({}, 'family')).toEqual(DEFAULT_PLAN_LIMITS.family);
77
+ expect(normalizeLimits(undefined, 'support')).toEqual(DEFAULT_PLAN_LIMITS.support);
78
+ expect(normalizeLimits(null, 'support')).toEqual(DEFAULT_PLAN_LIMITS.support);
79
+ expect(normalizeLimits({}, 'support')).toEqual(DEFAULT_PLAN_LIMITS.support);
80
80
  });
81
81
 
82
82
  it('resolves an unknown plan code to free even when limits are stored', () => {
@@ -138,13 +138,20 @@ export const DEFAULT_PLAN_LIMITS: Readonly<Record<string, PlanLimits>> = {
138
138
  features: { cleanExport: false },
139
139
  retentionDays: 90
140
140
  },
141
- personal: {
142
- meters: { storageBytes: 50 * GB, aiJobs: 200, trackedTickers: 25, seats: 1 },
143
- features: { cleanExport: true },
144
- retentionDays: UNLIMITED
145
- },
146
- family: {
147
- meters: { storageBytes: 250 * GB, aiJobs: 600, trackedTickers: 100, seats: 5 },
141
+ /**
142
+ * The single paid plan, and it is a *support* tier rather than a product
143
+ * edition. `personal` and `family` were consolidated into it: two paid tiers
144
+ * imply a business deciding what to withhold from which customer, which is
145
+ * the wrong shape for a project asking people to chip in. One tier that
146
+ * grants everything removes that question entirely.
147
+ *
148
+ * Limits are the old `family` numbers — pooled seats, unlimited retention —
149
+ * except storage, which is 50 GB rather than 250. 250 GB was priced for a
150
+ * household paying €12; at €5 it is more object storage than the support is
151
+ * covering.
152
+ */
153
+ support: {
154
+ meters: { storageBytes: 50 * GB, aiJobs: 600, trackedTickers: 100, seats: 5 },
148
155
  features: { cleanExport: true },
149
156
  retentionDays: UNLIMITED
150
157
  },
@@ -30,7 +30,7 @@ export type { Entitlements } from './types';
30
30
 
31
31
  export { UsageMeter, currentPeriod } from './UsageMeter';
32
32
  export type { IUsageMeter } from './UsageMeter';
33
- export { getUsage, getUsageSnapshot, checkQuota, consumeQuota, releaseQuota, setUsage } from './usage';
33
+ export { getUsage, getUsageSnapshot, checkQuota, consumeQuota, recordUsage, releaseQuota, setUsage } from './usage';
34
34
  export type { QuotaRequest } from './usage';
35
35
 
36
36
  export { reconcileUsageMeters, runUsageReconciliation, RECONCILED_METERS } from './reconcile';
@@ -25,7 +25,7 @@ function entitlements(overrides: Partial<Entitlements> = {}): Entitlements {
25
25
  planCode: 'free',
26
26
  status: null,
27
27
  limits: normalizeLimits(undefined, 'free'),
28
- upgradeTo: 'personal',
28
+ upgradeTo: 'support',
29
29
  ...overrides
30
30
  };
31
31
  }
@@ -68,7 +68,7 @@ describe('loadEntitlements', () => {
68
68
 
69
69
  describe('requireEntitlement', () => {
70
70
  it('passes a plan that includes the feature', async () => {
71
- mockGetEntitlements.mockResolvedValue(entitlements({ planCode: 'personal', limits: normalizeLimits(undefined, 'personal') }));
71
+ mockGetEntitlements.mockResolvedValue(entitlements({ planCode: 'support', limits: normalizeLimits(undefined, 'support') }));
72
72
  const next = jest.fn();
73
73
 
74
74
  await requireEntitlement('cleanExport')(mockReq(), {} as Response, next);
@@ -84,7 +84,7 @@ describe('requireEntitlement', () => {
84
84
  expect(next).toHaveBeenCalledWith(
85
85
  expect.objectContaining({
86
86
  statusCode: 402,
87
- details: { code: 'FEATURE_NOT_IN_PLAN', feature: 'cleanExport', planCode: 'free', upgradeTo: 'personal' }
87
+ details: { code: 'FEATURE_NOT_IN_PLAN', feature: 'cleanExport', planCode: 'free', upgradeTo: 'support' }
88
88
  })
89
89
  );
90
90
  });
@@ -142,7 +142,7 @@ describe('retentionFloor', () => {
142
142
 
143
143
  it('returns null for an unlimited plan, so no $match is added', async () => {
144
144
  mockGetEntitlements.mockResolvedValue(
145
- entitlements({ planCode: 'family', limits: normalizeLimits({ retentionDays: UNLIMITED }, 'family') })
145
+ entitlements({ planCode: 'support', limits: normalizeLimits({ retentionDays: UNLIMITED }, 'support') })
146
146
  );
147
147
 
148
148
  expect(await retentionFloor('user@example.com', now)).toBeNull();
@@ -163,7 +163,7 @@ describe('retentionFloor across several subjects', () => {
163
163
  it('takes the most generous floor, so a paid group lifts it for every member', () => {
164
164
  mockGetEntitlements.mockImplementation(async (email: string) =>
165
165
  email === 'owner@example.com'
166
- ? entitlements({ planCode: 'family', limits: normalizeLimits({ retentionDays: UNLIMITED }, 'family') })
166
+ ? entitlements({ planCode: 'support', limits: normalizeLimits({ retentionDays: UNLIMITED }, 'support') })
167
167
  : entitlements()
168
168
  );
169
169
 
@@ -18,7 +18,7 @@ jest.mock('../logging/logger', () => ({
18
18
 
19
19
  import logger from '../logging/logger';
20
20
  import { UsageMeter, currentPeriod } from './UsageMeter';
21
- import { checkQuota, consumeQuota, getUsage, getUsageSnapshot, releaseQuota, setUsage } from './usage';
21
+ import { checkQuota, consumeQuota, getUsage, getUsageSnapshot, recordUsage, releaseQuota, setUsage } from './usage';
22
22
  import { Entitlements } from './types';
23
23
  import { UNLIMITED, normalizeLimits } from './definitions';
24
24
 
@@ -241,6 +241,53 @@ describe('releaseQuota', () => {
241
241
  });
242
242
  });
243
243
 
244
+ describe('recordUsage', () => {
245
+ it('increments a meter that does not exist yet', async () => {
246
+ await recordUsage('user@example.com', 'storageBytes', 4_096);
247
+
248
+ expect(await getUsage('user@example.com', 'storageBytes')).toBe(4_096);
249
+ });
250
+
251
+ it('records past the limit rather than refusing bytes already on disk', async () => {
252
+ // The gate ran at the signed-URL request; these bytes are in MinIO whatever
253
+ // the counter says. Rolling the increment back — what consumeQuota would do
254
+ // — would understate real storage and let the next upload through too.
255
+ await setUsage('user@example.com', 'storageBytes', 900);
256
+
257
+ await recordUsage('user@example.com', 'storageBytes', 500);
258
+
259
+ expect(await getUsage('user@example.com', 'storageBytes')).toBe(1_400);
260
+ // And the next request is the one that is refused.
261
+ await expect(
262
+ checkQuota({ subjectId: 'user@example.com', meter: 'storageBytes', amount: 1, entitlements: entitlements() })
263
+ ).rejects.toMatchObject({ statusCode: 402 });
264
+ });
265
+
266
+ it('does not lose concurrent increments', async () => {
267
+ await Promise.all(Array.from({ length: 10 }, () => recordUsage('user@example.com', 'storageBytes', 10)));
268
+
269
+ expect(await getUsage('user@example.com', 'storageBytes')).toBe(100);
270
+ });
271
+
272
+ it('ignores a non-positive amount instead of writing a no-op row', async () => {
273
+ await recordUsage('user@example.com', 'storageBytes', 0);
274
+ await recordUsage('user@example.com', 'storageBytes', -50);
275
+
276
+ expect(await UsageMeter.countDocuments({ subjectId: 'user@example.com' })).toBe(0);
277
+ });
278
+
279
+ it('never throws — the resource exists and the caller is on its success path', async () => {
280
+ const failing = jest
281
+ .spyOn(UsageMeter, 'updateOne')
282
+ .mockRejectedValueOnce(new Error('connection lost') as never);
283
+
284
+ await expect(recordUsage('user@example.com', 'storageBytes', 10)).resolves.toBeUndefined();
285
+ expect(logger.warn).toHaveBeenCalledWith('Failed to record usage', expect.objectContaining({ meter: 'storageBytes' }));
286
+
287
+ failing.mockRestore();
288
+ });
289
+ });
290
+
244
291
  describe('getUsageSnapshot', () => {
245
292
  it('reports every meter, defaulting the untouched ones to zero', async () => {
246
293
  await setUsage('user@example.com', 'storageBytes', 42);
@@ -177,3 +177,47 @@ export async function setUsage(
177
177
  { upsert: true, setDefaultsOnInsert: true }
178
178
  );
179
179
  }
180
+
181
+ /**
182
+ * Record usage that has already happened, without a limit check.
183
+ *
184
+ * The storage gate runs on the *signed-URL request* — the only point where a
185
+ * file's size is known before its bytes are written — so by the time the photo
186
+ * record is created the object is already in MinIO. Neither of the existing
187
+ * primitives fits that moment: `checkQuota` records nothing, and `consumeQuota`
188
+ * would reject and roll back, leaving the meter understating bytes that are
189
+ * genuinely stored while the user is refused a record for storage they have
190
+ * already paid for.
191
+ *
192
+ * So this increments past the limit deliberately. A cumulative meter can only
193
+ * be pushed over by a race or a stale entitlement, both bounded by one upload,
194
+ * and the gate then refuses the *next* one — which is the correct behaviour:
195
+ * the ceiling stops new writes, it does not un-store bytes.
196
+ *
197
+ * Never throws, for the same reason `releaseQuota` does not: the resource
198
+ * exists, the caller is on its success path, and a failed counter update is
199
+ * repaired by the nightly reconciliation this meter is derived for.
200
+ */
201
+ export async function recordUsage(
202
+ subjectId: string,
203
+ meter: MeterKey,
204
+ amount = 1,
205
+ now?: Date
206
+ ): Promise<void> {
207
+ if (amount <= 0) return;
208
+
209
+ try {
210
+ await UsageMeter.updateOne(
211
+ { subjectId: subjectId.toLowerCase(), meter, period: currentPeriod(meter, now) },
212
+ { $inc: { count: amount } },
213
+ { upsert: true, setDefaultsOnInsert: true }
214
+ );
215
+ } catch (err) {
216
+ logger.warn('Failed to record usage', {
217
+ subjectId,
218
+ meter,
219
+ amount,
220
+ error: (err as Error)?.message
221
+ });
222
+ }
223
+ }
@@ -26,7 +26,7 @@ describe('PaymentRequiredError', () => {
26
26
  used: 10,
27
27
  limit: 10,
28
28
  planCode: 'free',
29
- upgradeTo: 'personal'
29
+ upgradeTo: 'support'
30
30
  });
31
31
 
32
32
  expect(error.statusCode).toBe(402);
@@ -37,7 +37,7 @@ describe('PaymentRequiredError', () => {
37
37
  used: 10,
38
38
  limit: 10,
39
39
  planCode: 'free',
40
- upgradeTo: 'personal'
40
+ upgradeTo: 'support'
41
41
  });
42
42
  });
43
43
 
package/src/index.ts CHANGED
@@ -31,7 +31,7 @@ export * from './entitlements';
31
31
  export * from './groups';
32
32
 
33
33
  // Middleware
34
- export { authenticateToken } from './middleware/authMiddleware';
34
+ export { authenticateToken, optionalAuth } from './middleware/authMiddleware';
35
35
  export {
36
36
  requireInternalServiceToken,
37
37
  internalServiceTokenBypass,
@@ -1,6 +1,6 @@
1
1
  import { Request, Response } from 'express';
2
2
  import jwt from 'jsonwebtoken';
3
- import { authenticateToken } from './authMiddleware';
3
+ import { authenticateToken, optionalAuth } from './authMiddleware';
4
4
  import { UserPayload } from '../types/auth';
5
5
 
6
6
  function mockReq(overrides: Partial<Request> = {}): Request {
@@ -102,3 +102,72 @@ describe('authenticateToken', () => {
102
102
  expect(res.status).toHaveBeenCalledWith(401);
103
103
  });
104
104
  });
105
+
106
+ describe('optionalAuth', () => {
107
+ const ORIGINAL_ENV = process.env;
108
+ const next = jest.fn();
109
+
110
+ beforeEach(() => {
111
+ process.env = { ...ORIGINAL_ENV, JWT_SECRET: 'test-secret' };
112
+ next.mockClear();
113
+ });
114
+
115
+ afterAll(() => {
116
+ process.env = ORIGINAL_ENV;
117
+ });
118
+
119
+ const payload: UserPayload = { id: '1', email: 'user@example.com', name: 'Test User' };
120
+
121
+ it('attaches the user when the token is valid', () => {
122
+ const req = mockReq({ cookies: { access_token: jwt.sign(payload, 'test-secret') } });
123
+
124
+ optionalAuth(req, mockRes(), next);
125
+
126
+ expect(next).toHaveBeenCalledTimes(1);
127
+ expect(req.user).toMatchObject({ email: 'user@example.com' });
128
+ });
129
+
130
+ it('reads a bearer token too', () => {
131
+ const req = mockReq({ headers: { authorization: `Bearer ${jwt.sign(payload, 'test-secret')}` } });
132
+
133
+ optionalAuth(req, mockRes(), next);
134
+
135
+ expect(req.user).toMatchObject({ email: 'user@example.com' });
136
+ });
137
+
138
+ it('continues anonymously when there is no token', () => {
139
+ const req = mockReq();
140
+ const res = mockRes();
141
+
142
+ optionalAuth(req, res, next);
143
+
144
+ expect(next).toHaveBeenCalledTimes(1);
145
+ expect(req.user).toBeUndefined();
146
+ expect(res.status).not.toHaveBeenCalled();
147
+ });
148
+
149
+ it('treats an expired or forged token as no token rather than as a 401', () => {
150
+ // The caller asked for public data. Refusing it because a stale cookie is
151
+ // lying around would break the landing page for exactly the people most
152
+ // likely to have one.
153
+ const req = mockReq({ cookies: { access_token: jwt.sign(payload, 'wrong-secret') } });
154
+ const res = mockRes();
155
+
156
+ optionalAuth(req, res, next);
157
+
158
+ expect(next).toHaveBeenCalledTimes(1);
159
+ expect(req.user).toBeUndefined();
160
+ expect(res.status).not.toHaveBeenCalled();
161
+ });
162
+
163
+ it('continues anonymously when JWT_SECRET is not configured', () => {
164
+ delete process.env.JWT_SECRET;
165
+ const req = mockReq({ cookies: { access_token: 'anything' } });
166
+ const res = mockRes();
167
+
168
+ optionalAuth(req, res, next);
169
+
170
+ expect(next).toHaveBeenCalledTimes(1);
171
+ expect(res.status).not.toHaveBeenCalled();
172
+ });
173
+ });
@@ -26,4 +26,32 @@ export function authenticateToken(req: Request, res: Response, next: NextFunctio
26
26
  } catch (error) {
27
27
  res.status(401).json({ success: false, message: 'Invalid or expired token' });
28
28
  }
29
- }
29
+ }
30
+ /**
31
+ * Attaches the user when a valid token is present, and never rejects.
32
+ *
33
+ * For routes whose response is public but *richer* when signed in — the plan
34
+ * catalog is the case this exists for: a signed-out visitor on the landing
35
+ * page must be able to read prices, while a signed-in one additionally gets
36
+ * the hidden plan they are actually on.
37
+ *
38
+ * An invalid or expired token is treated as no token rather than as a 401. The
39
+ * caller asked for public data; refusing it because a stale cookie is lying
40
+ * around would break the landing page for exactly the people most likely to
41
+ * have one.
42
+ */
43
+ export function optionalAuth(req: Request, _res: Response, next: NextFunction): void {
44
+ try {
45
+ const JWT_SECRET = requireEnv('JWT_SECRET');
46
+ const token = req.cookies?.access_token || req.headers.authorization?.replace('Bearer ', '');
47
+
48
+ if (token) {
49
+ const user = jwt.verify(token, JWT_SECRET) as UserPayload;
50
+ req.user = user;
51
+ req.userGroups = user.groups ?? [];
52
+ }
53
+ } catch {
54
+ // Deliberately ignored — see above.
55
+ }
56
+ next();
57
+ }
@@ -33,7 +33,7 @@ function buildApp() {
33
33
  used: 1073741824,
34
34
  limit: 1073741824,
35
35
  planCode: 'free',
36
- upgradeTo: 'personal'
36
+ upgradeTo: 'support'
37
37
  })
38
38
  )
39
39
  );
@@ -172,7 +172,7 @@ describe('errorHandler', () => {
172
172
  used: 1073741824,
173
173
  limit: 1073741824,
174
174
  planCode: 'free',
175
- upgradeTo: 'personal'
175
+ upgradeTo: 'support'
176
176
  });
177
177
  });
178
178