@teleporthq/teleport-plugin-next-workflows 0.45.5 → 0.45.8

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.
Files changed (35) hide show
  1. package/__tests__/account-delete-password-check.test.ts +39 -0
  2. package/__tests__/analytics-track-outcome.test.ts +56 -0
  3. package/__tests__/public-step-guards.test.ts +26 -0
  4. package/__tests__/server-job-self-calls-absolute.test.ts +430 -0
  5. package/dist/cjs/account-delete-route-generator.d.ts.map +1 -1
  6. package/dist/cjs/account-delete-route-generator.js +17 -8
  7. package/dist/cjs/account-delete-route-generator.js.map +1 -1
  8. package/dist/cjs/nodes/analytics/analytics-track-outcome.d.ts.map +1 -1
  9. package/dist/cjs/nodes/analytics/analytics-track-outcome.js +12 -0
  10. package/dist/cjs/nodes/analytics/analytics-track-outcome.js.map +1 -1
  11. package/dist/cjs/public-step-guards.d.ts +2 -2
  12. package/dist/cjs/public-step-guards.d.ts.map +1 -1
  13. package/dist/cjs/public-step-guards.js +11 -8
  14. package/dist/cjs/public-step-guards.js.map +1 -1
  15. package/dist/cjs/publish-refusals.js +1 -1
  16. package/dist/cjs/publish-refusals.js.map +1 -1
  17. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  18. package/dist/esm/account-delete-route-generator.d.ts.map +1 -1
  19. package/dist/esm/account-delete-route-generator.js +17 -8
  20. package/dist/esm/account-delete-route-generator.js.map +1 -1
  21. package/dist/esm/nodes/analytics/analytics-track-outcome.d.ts.map +1 -1
  22. package/dist/esm/nodes/analytics/analytics-track-outcome.js +12 -0
  23. package/dist/esm/nodes/analytics/analytics-track-outcome.js.map +1 -1
  24. package/dist/esm/public-step-guards.d.ts +2 -2
  25. package/dist/esm/public-step-guards.d.ts.map +1 -1
  26. package/dist/esm/public-step-guards.js +11 -8
  27. package/dist/esm/public-step-guards.js.map +1 -1
  28. package/dist/esm/publish-refusals.js +1 -1
  29. package/dist/esm/publish-refusals.js.map +1 -1
  30. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +2 -2
  32. package/src/account-delete-route-generator.ts +17 -8
  33. package/src/nodes/analytics/analytics-track-outcome.ts +12 -0
  34. package/src/public-step-guards.ts +14 -8
  35. package/src/publish-refusals.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teleporthq/teleport-plugin-next-workflows",
3
- "version": "0.45.5",
3
+ "version": "0.45.8",
4
4
  "description": "A plugin for workflow code generation in Next.js projects.",
5
5
  "author": "teleportHQ",
6
6
  "license": "MIT",
@@ -69,5 +69,5 @@
69
69
  "@teleporthq/teleport-shared": "^0.45.0",
70
70
  "@teleporthq/teleport-types": "^0.45.0"
71
71
  },
72
- "gitHead": "3af9d41f936652db5b37d0a40ebcc78bdfd685a5"
72
+ "gitHead": "c80bb3e31711590476bd0f8f6c6962948aae20eb"
73
73
  }
@@ -335,19 +335,28 @@ function readRequestPassword(req) {
335
335
  return String(body.password);
336
336
  }
337
337
 
338
- // The sign-in check (utils/auth/hash-password.js verifyPassword): bcrypt over
339
- // the SHA-256 of the password and AUTH_PEPPER. An account without a stored
340
- // password (a social sign-in) matches no password.
338
+ // The sign-in check (utils/auth/hash-password.js checkPassword): bcrypt over
339
+ // the SHA-256 of the password and a pepper, the project's own
340
+ // (AUTH_PEPPER_SECRET) first, then the one every earlier hash used
341
+ // (AUTH_PEPPER). An account without a stored password (a social sign-in)
342
+ // matches no password.
341
343
  function passwordMatches(password, storedHash) {
342
344
  if (!storedHash || password.length > 1000) { return false; }
343
345
  var bcrypt = require('bcryptjs');
344
346
  var crypto = require('crypto');
345
- var prepared = crypto.createHash('sha256').update(password + (process.env.AUTH_PEPPER || '')).digest('base64');
346
- try {
347
- return bcrypt.compareSync(prepared, String(storedHash));
348
- } catch (e) {
349
- return false;
347
+ var secret = process.env.AUTH_PEPPER_SECRET;
348
+ var peppers = typeof secret === 'string' && secret && secret.indexOf('teleporthq.secrets.') !== 0
349
+ ? [secret, process.env.AUTH_PEPPER || '']
350
+ : [process.env.AUTH_PEPPER || ''];
351
+ for (var i = 0; i < peppers.length; i++) {
352
+ var prepared = crypto.createHash('sha256').update(password + peppers[i]).digest('base64');
353
+ try {
354
+ if (bcrypt.compareSync(prepared, String(storedHash))) { return true; }
355
+ } catch (e) {
356
+ return false;
357
+ }
350
358
  }
359
+ return false;
351
360
  }
352
361
 
353
362
  ${generateFillTemplateFn()}
@@ -20,6 +20,13 @@ import { NodeHandlerGenerator } from '../types'
20
20
  * the visitor id is the tracker's alone: a browser may still hold a persistent
21
21
  * id from before its consent was withdrawn.
22
22
  *
23
+ * The service takes exactly the money outcomes from a server, each with the
24
+ * order or booking id it dedupes a retried report on, and every other outcome
25
+ * from a browser only. So on the server any other name is refused as
26
+ * `browser-only` (an automation, a webhook or a cron has no visitor's page to
27
+ * report it from), and a money outcome without an id as `no-outcome-id`: an id
28
+ * made up here would count a retried report twice. Both say why in the logs.
29
+ *
23
30
  * Never throws: an outcome that cannot be reported answers `{ sent: false }`
24
31
  * and the workflow continues — analytics must never cost a sale.
25
32
  */
@@ -57,6 +64,10 @@ const SERVER_BODY = `async function analytics_track_outcome(config, context) {
57
64
  if (!name) {
58
65
  return { sent: false, reason: 'no-name' };
59
66
  }
67
+ if (${JSON.stringify(SERVER_ONLY_OUTCOMES)}.indexOf(name) === -1) {
68
+ console.warn('[analytics] the "' + name + '" outcome was not reported: only a visitor\\'s browser can report it, never a server step');
69
+ return { sent: false, reason: 'browser-only' };
70
+ }
60
71
  // \`globalThis.process\` (member access): webpack rewrites a bare \`process\`.
61
72
  var env = globalThis.process && globalThis.process.env;
62
73
  var baseUrl = (env && env.NEXT_PUBLIC_TELEPORT_ANALYTICS_URL) || '';
@@ -67,6 +78,7 @@ const SERVER_BODY = `async function analytics_track_outcome(config, context) {
67
78
  }
68
79
  // The service dedupes on this id and drops an outcome without one.
69
80
  if (config.outcomeId == null || config.outcomeId === '') {
81
+ console.warn('[analytics] the "' + name + '" outcome was not reported: it has no order or booking id');
70
82
  return { sent: false, reason: 'no-outcome-id' };
71
83
  }
72
84
  var ids = context && context.__tpa && typeof context.__tpa === 'object' ? context.__tpa : {};
@@ -103,12 +103,18 @@ const adviceFor = (staffRoles: string[]): string =>
103
103
  const SHORT_ADVICE =
104
104
  'Let only an admin role start it (a role your admin-panel pages admit), or move it into a scheduled, signed webhook or automation workflow, then publish again.'
105
105
 
106
+ /** What to change on a site without accounts: no role can be required until it has them. */
107
+ const NO_ACCOUNTS_ADVICE =
108
+ 'Your site has no sign-in, so no role can be required yet: enable authentication and let only an admin role start it, or move it into a scheduled, signed webhook or automation workflow, then publish again.'
109
+
106
110
  /** One line of plain prose, for the person publishing. */
107
111
  export const describePublicStepRefusals = (
108
112
  refusals: PublicStepRefusal[],
109
- staffRoles: string[] = [TableAccess.ADMIN_ROLE]
113
+ staffRoles: string[] = [TableAccess.ADMIN_ROLE],
114
+ hasAccounts = true
110
115
  ): string => {
111
- const advice = adviceFor(staffRoles)
116
+ const advice = hasAccounts ? adviceFor(staffRoles) : NO_ACCOUNTS_ADVICE
117
+ const shortAdvice = hasAccounts ? SHORT_ADVICE : NO_ACCOUNTS_ADVICE
112
118
  const listed = refusals.slice(0, LISTED_STEPS).map(describe)
113
119
  const more = refusals.length - listed.length
114
120
  const subject =
@@ -121,10 +127,10 @@ export const describePublicStepRefusals = (
121
127
  )}${more > 0 ? `; and ${more} more` : ''}.`
122
128
  for (const candidate of [
123
129
  `${subject} ${advice}`,
124
- `${subject} ${SHORT_ADVICE}`,
130
+ `${subject} ${shortAdvice}`,
125
131
  `On your site the step ${describe(refusals[0])}${
126
132
  refusals.length > 1 ? `, and ${refusals.length - 1} more` : ''
127
- }. ${SHORT_ADVICE}`,
133
+ }. ${shortAdvice}`,
128
134
  ]) {
129
135
  if (candidate.length <= MAX_SENTENCE_LENGTH) {
130
136
  return candidate
@@ -132,7 +138,7 @@ export const describePublicStepRefusals = (
132
138
  }
133
139
  return `On your site ${
134
140
  refusals.length === 1 ? 'a step' : `${refusals.length} steps`
135
- } meant only for an admin or your own server (a push notification or a stored-file step) can be started by others. ${SHORT_ADVICE}`
141
+ } meant only for an admin or your own server (a push notification or a stored-file step) can be started by others. ${shortAdvice}`
136
142
  }
137
143
 
138
144
  /**
@@ -147,11 +153,11 @@ export class PublicStepRefusalError extends Error {
147
153
  readonly code = PUBLIC_STEP_REFUSAL_CODE
148
154
  readonly refusals: PublicStepRefusal[]
149
155
 
150
- constructor(refusals: PublicStepRefusal[], staffRoles?: string[]) {
156
+ constructor(refusals: PublicStepRefusal[], staffRoles?: string[], hasAccounts?: boolean) {
151
157
  super(
152
158
  `Publishing refused: ${JSON.stringify({
153
159
  code: PUBLIC_STEP_REFUSAL_CODE,
154
- message: describePublicStepRefusals(refusals, staffRoles),
160
+ message: describePublicStepRefusals(refusals, staffRoles, hasAccounts),
155
161
  steps: refusals.map((r) => ({ owner: r.ownerId, node: r.nodeId, type: r.nodeType })),
156
162
  })}`
157
163
  )
@@ -443,6 +449,6 @@ export const assertNoPublicRestrictedSteps = (
443
449
  const staffRoles = TableAccess.resolveStaffRoles(authentication)
444
450
  const refusals = findPublicRestrictedSteps(workflows, { staffRoles })
445
451
  if (refusals.length > 0) {
446
- throw new PublicStepRefusalError(refusals, staffRoles)
452
+ throw new PublicStepRefusalError(refusals, staffRoles, authentication?.enabled === true)
447
453
  }
448
454
  }
@@ -168,7 +168,7 @@ const publicStepRefusals = (
168
168
  ownerId: refusal.ownerId,
169
169
  ownerKind: refusal.ownerKind,
170
170
  nodeId: refusal.nodeId,
171
- message: describePublicStepRefusals([refusal], staffRoles),
171
+ message: describePublicStepRefusals([refusal], staffRoles, authentication?.enabled === true),
172
172
  }))
173
173
  }
174
174