thumbgate 1.4.0 → 1.4.1

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/src/api/server.js CHANGED
@@ -1020,6 +1020,7 @@ function loadPublicMarketingTemplateHtml(templatePath, runtimeConfig, pageContex
1020
1020
  '__AUTOMATION_REPORT_URL__': 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/proof/automation/report.json',
1021
1021
  '__GTM_PLAN_URL__': 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/GO_TO_MARKET_REVENUE_WEDGE_2026-03.md',
1022
1022
  '__GITHUB_URL__': 'https://github.com/IgorGanapolsky/ThumbGate',
1023
+ '__POSTHOG_API_KEY__': runtimeConfig.posthogApiKey || '',
1023
1024
  });
1024
1025
  }
1025
1026
 
@@ -2153,6 +2154,11 @@ function getExpectedApiKey() {
2153
2154
  return configured;
2154
2155
  }
2155
2156
 
2157
+ function getExpectedOperatorKey() {
2158
+ const key = String(process.env.THUMBGATE_OPERATOR_KEY || '').trim();
2159
+ return key || null;
2160
+ }
2161
+
2156
2162
  function isAuthorized(req, expected) {
2157
2163
  if (!expected) return true;
2158
2164
  const token = extractApiKey(req);
@@ -2204,6 +2210,19 @@ function isStaticAdminAuthorized(req, expected) {
2204
2210
  return extractApiKey(req) === expected;
2205
2211
  }
2206
2212
 
2213
+ /**
2214
+ * Billing summary guard: accepts either the static admin key OR the operator key.
2215
+ * The operator key (THUMBGATE_OPERATOR_KEY) allows read-only billing data access
2216
+ * without exposing the full admin key to CLI clients.
2217
+ */
2218
+ function isBillingSummaryAuthorized(req, expectedAdminKey, expectedOperatorKey) {
2219
+ if (!expectedAdminKey && !expectedOperatorKey) return true;
2220
+ const token = extractApiKey(req);
2221
+ if (expectedAdminKey && token === expectedAdminKey) return true;
2222
+ if (expectedOperatorKey && token === expectedOperatorKey) return true;
2223
+ return false;
2224
+ }
2225
+
2207
2226
  function extractTags(input) {
2208
2227
  if (Array.isArray(input)) return input;
2209
2228
  if (typeof input === 'string') {
@@ -2318,6 +2337,7 @@ function resolveDocumentImportFilePath(inputPath, options = {}) {
2318
2337
 
2319
2338
  function createApiServer() {
2320
2339
  const expectedApiKey = getExpectedApiKey();
2340
+ const expectedOperatorKey = getExpectedOperatorKey();
2321
2341
 
2322
2342
  return http.createServer(async (req, res) => {
2323
2343
  const parsed = new URL(req.url, 'http://localhost');
@@ -4540,14 +4560,14 @@ async function addContext(){
4540
4560
  return;
4541
4561
  }
4542
4562
 
4543
- // GET /v1/billing/summary — admin-only operational billing summary
4563
+ // GET /v1/billing/summary — operator billing summary (admin key or operator key)
4544
4564
  if (req.method === 'GET' && pathname === '/v1/billing/summary') {
4545
- if (!isStaticAdminAuthorized(req, expectedApiKey)) {
4565
+ if (!isBillingSummaryAuthorized(req, expectedApiKey, expectedOperatorKey)) {
4546
4566
  sendProblem(res, {
4547
4567
  type: PROBLEM_TYPES.FORBIDDEN,
4548
4568
  title: 'Forbidden',
4549
4569
  status: 403,
4550
- detail: 'Admin API key required for this endpoint.',
4570
+ detail: 'Admin or operator API key required for this endpoint.',
4551
4571
  });
4552
4572
  return;
4553
4573
  }