@teleporthq/teleport-plugin-next-workflows 0.45.6 → 0.45.9

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.
@@ -11,12 +11,13 @@ exports.generateNewsletterUnsubscribeRoute = exports.generateNewsletterConfirmRo
11
11
  * `footer`, `popup`, `form`), else `block`; errors are logged by code and
12
12
  * message only (never a database row); every answer is `{ ok: true, pending }`, after at least
13
13
  * `NEWSLETTER_MIN_RESPONSE_MS`, whatever happened to the address.
14
- * - `confirm` — POST `{ token }` → `{ ok, email? }`.
15
- * - `unsubscribe` — POST `{ token }` → `{ ok, email? }`; a GET with `?t=`
16
- * (a click in a mail client) answers a small page whose button POSTs it, so a
17
- * link scanner that opens every link unsubscribes nobody.
14
+ * - `confirm` and `unsubscribe` — POST `{ token }` → `{ ok, email? }`; a GET
15
+ * with `?t=` (a click in a mail client) answers a small page whose button
16
+ * POSTs it, so a link scanner that opens every link confirms and
17
+ * unsubscribes nobody.
18
18
  */
19
19
  const security_runtime_code_1 = require("../security-runtime/security-runtime-code");
20
+ const helper_code_1 = require("./helper-code");
20
21
  const newsletter_scope_1 = require("./newsletter-scope");
21
22
  /** From `pages/api/newsletter/` to the project root. */
22
23
  const RELATIVE_PREFIX = '../../..';
@@ -79,32 +80,7 @@ ${methodGuard(['POST'])}
79
80
  };
80
81
  `;
81
82
  exports.generateNewsletterSubscribeRoute = generateNewsletterSubscribeRoute;
82
- const generateNewsletterConfirmRoute = () => `${ROUTE_PREAMBLE}
83
-
84
- module.exports = async function handler(req, res) {
85
- ${methodGuard(['POST'])}
86
- if (!(await __security.rateLimitRequest(req, res, { scope: 'newsletter-confirm', limit: ${exports.NEWSLETTER_LINK_VISITOR_LIMIT}, windowSeconds: 3600 }))) {
87
- return;
88
- }
89
- var body = readBody(req);
90
- var token = typeof body.token === 'string' ? body.token : '';
91
- if (!token) {
92
- return res.status(200).json({ ok: false });
93
- }
94
- try {
95
- var result = await __newsletter.withNewsletterDb(function (db) {
96
- return __newsletter.confirmNewsletter({ db: db, token: token, req: req });
97
- });
98
- return res.status(200).json(result.ok ? { ok: true, email: result.email } : { ok: false });
99
- } catch (err) {
100
- return __security.sendClientError(res, 503, 'We could not confirm the subscription. Please try again in a moment.', __newsletter.safeError(err));
101
- }
102
- };
103
- `;
104
- exports.generateNewsletterConfirmRoute = generateNewsletterConfirmRoute;
105
- const generateNewsletterUnsubscribeRoute = () => `${ROUTE_PREAMBLE}
106
-
107
- function escapeHtml(value) {
83
+ const LINK_PAGE_CODE = `function escapeHtml(value) {
108
84
  return String(value == null ? '' : value)
109
85
  .replace(/&/g, '&')
110
86
  .replace(/</g, '&lt;')
@@ -128,7 +104,64 @@ function sendPage(res, status, title, message, form) {
128
104
  'button{font:inherit;padding:10px 18px;border:0;border-radius:6px;background:#111;color:#fff;cursor:pointer}</style>' +
129
105
  '</head><body><h1>' + escapeHtml(title) + '</h1><p>' + escapeHtml(message) + '</p>' + (form || '') + '</body></html>'
130
106
  );
131
- }
107
+ }`;
108
+ const CONFIRM_LINK_DAYS = helper_code_1.newsletterTokenSpec.confirmMaxAgeSeconds / 86400;
109
+ const generateNewsletterConfirmRoute = () => `${ROUTE_PREAMBLE}
110
+
111
+ ${LINK_PAGE_CODE}
112
+
113
+ var NOT_A_LINK = 'This confirmation link does not work. A link works for ${CONFIRM_LINK_DAYS} days: sign up again to get a new one.';
114
+
115
+ module.exports = async function handler(req, res) {
116
+ ${methodGuard(['GET', 'POST'])}
117
+ if (!(await __security.rateLimitRequest(req, res, { scope: 'newsletter-confirm', limit: ${exports.NEWSLETTER_LINK_VISITOR_LIMIT}, windowSeconds: 3600 }))) {
118
+ return;
119
+ }
120
+ if (req.method === 'GET') {
121
+ // Opening the link changes nothing (a mail client's link scanner opens it
122
+ // too, and double opt-in means the person's own action): the button does.
123
+ var linkToken = req.query && typeof req.query.t === 'string' ? req.query.t : '';
124
+ if (!__newsletter.verifyNewsletterToken(linkToken, 'confirm')) {
125
+ return sendPage(res, 200, 'Link not valid', NOT_A_LINK, '');
126
+ }
127
+ return sendPage(
128
+ res,
129
+ 200,
130
+ 'Confirm your subscription',
131
+ 'Receive the newsletter at this address?',
132
+ '<form method="post" action="${newsletter_scope_1.NEWSLETTER_CONFIRM_URL}"><input type="hidden" name="t" value="' +
133
+ escapeHtml(linkToken) + '"><button type="submit">Confirm subscription</button></form>'
134
+ );
135
+ }
136
+ var body = readBody(req);
137
+ var fromForm = String((req.headers && req.headers['content-type']) || '').indexOf('application/x-www-form-urlencoded') !== -1;
138
+ var token = typeof body.token === 'string' ? body.token : typeof body.t === 'string' ? body.t : '';
139
+ var result = { ok: false };
140
+ try {
141
+ if (token) {
142
+ result = await __newsletter.withNewsletterDb(function (db) {
143
+ return __newsletter.confirmNewsletter({ db: db, token: token, req: req });
144
+ });
145
+ }
146
+ } catch (err) {
147
+ if (fromForm) {
148
+ console.error('[newsletter] confirm failed:', __newsletter.safeError(err));
149
+ return sendPage(res, 503, 'Please try again', 'We could not confirm your subscription just now. Please try again in a moment.', '');
150
+ }
151
+ return __security.sendClientError(res, 503, 'We could not confirm the subscription. Please try again in a moment.', __newsletter.safeError(err));
152
+ }
153
+ if (fromForm) {
154
+ return result.ok
155
+ ? sendPage(res, 200, "You're subscribed", 'Thank you for confirming. You will receive the newsletter at this address.', '')
156
+ : sendPage(res, 200, 'Link not valid', NOT_A_LINK, '');
157
+ }
158
+ return res.status(200).json(result.ok ? { ok: true, email: result.email } : { ok: false });
159
+ };
160
+ `;
161
+ exports.generateNewsletterConfirmRoute = generateNewsletterConfirmRoute;
162
+ const generateNewsletterUnsubscribeRoute = () => `${ROUTE_PREAMBLE}
163
+
164
+ ${LINK_PAGE_CODE}
132
165
 
133
166
  var NOT_A_LINK = 'This unsubscribe link does not work. Use the link in a newer email from us.';
134
167
 
@@ -1 +1 @@
1
- {"version":3,"file":"routes-code.js","sourceRoot":"","sources":["../../../src/newsletter/routes-code.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;GAcG;AACH,qFAAoF;AACpF,yDAK2B;AAE3B,wDAAwD;AACxD,MAAM,eAAe,GAAG,UAAU,CAAA;AAElC,6DAA6D;AAChD,QAAA,6BAA6B,GAAG,EAAE,CAAA;AAE/C,MAAM,cAAc,GAAG,6BAA6B,IAAA,gDAAwB,EAAC,eAAe,CAAC;8BAC/D,IAAA,4CAAyB,EAAC,eAAe,CAAC;;;;;;;;EAQtE,CAAA;AAEF,MAAM,WAAW,GAAG,CAAC,OAAiB,EAAU,EAAE,CAAC,SAAS,OAAO;KAChE,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,mBAAmB,MAAM,GAAG,CAAC;KAC7C,IAAI,CAAC,MAAM,CAAC;8BACe,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;;IAE5C,CAAA;AAEG,MAAM,gCAAgC,GAAG,GAAW,EAAE,CAAC,GAAG,cAAc;;;EAG7E,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;OAChB,2CAAwB;mDACoB,2CAAwB;8EACG,2CAAwB,aAAa,2CAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiC1I,CAAA;AAvCY,QAAA,gCAAgC,oCAuC5C;AAEM,MAAM,8BAA8B,GAAG,GAAW,EAAE,CAAC,GAAG,cAAc;;;EAG3E,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;4FACqE,qCAA6B;;;;;;;;;;;;;;;;;CAiBxH,CAAA;AArBY,QAAA,8BAA8B,kCAqB1C;AAEM,MAAM,kCAAkC,GAAG,GAAW,EAAE,CAAC,GAAG,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+B/E,WAAW,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gGACkE,qCAA6B;;;;;;;;;;;;;;qCAcxF,6CAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4B9D,CAAA;AA1EY,QAAA,kCAAkC,sCA0E9C"}
1
+ {"version":3,"file":"routes-code.js","sourceRoot":"","sources":["../../../src/newsletter/routes-code.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;GAcG;AACH,qFAAoF;AACpF,+CAAmD;AACnD,yDAM2B;AAE3B,wDAAwD;AACxD,MAAM,eAAe,GAAG,UAAU,CAAA;AAElC,6DAA6D;AAChD,QAAA,6BAA6B,GAAG,EAAE,CAAA;AAE/C,MAAM,cAAc,GAAG,6BAA6B,IAAA,gDAAwB,EAAC,eAAe,CAAC;8BAC/D,IAAA,4CAAyB,EAAC,eAAe,CAAC;;;;;;;;EAQtE,CAAA;AAEF,MAAM,WAAW,GAAG,CAAC,OAAiB,EAAU,EAAE,CAAC,SAAS,OAAO;KAChE,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,mBAAmB,MAAM,GAAG,CAAC;KAC7C,IAAI,CAAC,MAAM,CAAC;8BACe,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;;IAE5C,CAAA;AAEG,MAAM,gCAAgC,GAAG,GAAW,EAAE,CAAC,GAAG,cAAc;;;EAG7E,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;OAChB,2CAAwB;mDACoB,2CAAwB;8EACG,2CAAwB,aAAa,2CAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiC1I,CAAA;AAvCY,QAAA,gCAAgC,oCAuC5C;AAED,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;EAwBrB,CAAA;AAEF,MAAM,iBAAiB,GAAG,iCAAmB,CAAC,oBAAoB,GAAG,KAAK,CAAA;AAEnE,MAAM,8BAA8B,GAAG,GAAW,EAAE,CAAC,GAAG,cAAc;;EAE3E,cAAc;;2EAE2D,iBAAiB;;;EAG1F,WAAW,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;4FAC8D,qCAA6B;;;;;;;;;;;;;;;qCAepF,yCAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4B1D,CAAA;AAnDY,QAAA,8BAA8B,kCAmD1C;AAEM,MAAM,kCAAkC,GAAG,GAAW,EAAE,CAAC,GAAG,cAAc;;EAE/E,cAAc;;;;;EAKd,WAAW,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gGACkE,qCAA6B;;;;;;;;;;;;;;qCAcxF,6CAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4B9D,CAAA;AAlDY,QAAA,kCAAkC,sCAkD9C"}
@@ -1 +1 @@
1
- {"version":3,"file":"analytics-track-outcome.d.ts","sourceRoot":"","sources":["../../../../src/nodes/analytics/analytics-track-outcome.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAkH/C,eAAO,MAAM,qBAAqB,EAAE,oBASnC,CAAA"}
1
+ {"version":3,"file":"analytics-track-outcome.d.ts","sourceRoot":"","sources":["../../../../src/nodes/analytics/analytics-track-outcome.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AA8H/C,eAAO,MAAM,qBAAqB,EAAE,oBASnC,CAAA"}
@@ -21,6 +21,13 @@ exports.analyticsTrackOutcome = void 0;
21
21
  * the visitor id is the tracker's alone: a browser may still hold a persistent
22
22
  * id from before its consent was withdrawn.
23
23
  *
24
+ * The service takes exactly the money outcomes from a server, each with the
25
+ * order or booking id it dedupes a retried report on, and every other outcome
26
+ * from a browser only. So on the server any other name is refused as
27
+ * `browser-only` (an automation, a webhook or a cron has no visitor's page to
28
+ * report it from), and a money outcome without an id as `no-outcome-id`: an id
29
+ * made up here would count a retried report twice. Both say why in the logs.
30
+ *
24
31
  * Never throws: an outcome that cannot be reported answers `{ sent: false }`
25
32
  * and the workflow continues — analytics must never cost a sale.
26
33
  */
@@ -56,6 +63,10 @@ const SERVER_BODY = `async function analytics_track_outcome(config, context) {
56
63
  if (!name) {
57
64
  return { sent: false, reason: 'no-name' };
58
65
  }
66
+ if (${JSON.stringify(SERVER_ONLY_OUTCOMES)}.indexOf(name) === -1) {
67
+ console.warn('[analytics] the "' + name + '" outcome was not reported: only a visitor\\'s browser can report it, never a server step');
68
+ return { sent: false, reason: 'browser-only' };
69
+ }
59
70
  // \`globalThis.process\` (member access): webpack rewrites a bare \`process\`.
60
71
  var env = globalThis.process && globalThis.process.env;
61
72
  var baseUrl = (env && env.NEXT_PUBLIC_TELEPORT_ANALYTICS_URL) || '';
@@ -66,6 +77,7 @@ const SERVER_BODY = `async function analytics_track_outcome(config, context) {
66
77
  }
67
78
  // The service dedupes on this id and drops an outcome without one.
68
79
  if (config.outcomeId == null || config.outcomeId === '') {
80
+ console.warn('[analytics] the "' + name + '" outcome was not reported: it has no order or booking id');
69
81
  return { sent: false, reason: 'no-outcome-id' };
70
82
  }
71
83
  var ids = context && context.__tpa && typeof context.__tpa === 'object' ? context.__tpa : {};
@@ -1 +1 @@
1
- {"version":3,"file":"analytics-track-outcome.js","sourceRoot":"","sources":["../../../../src/nodes/analytics/analytics-track-outcome.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,cAAc,CAAC,CAAA;AAEzD,MAAM,WAAW,GAAG;;;;;QAKZ,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;EAoB1C,CAAA;AAEF,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0DlB,CAAA;AAEW,QAAA,qBAAqB,GAAyB;IACzD,QAAQ,EAAE,yBAAyB;IACnC,YAAY,EAAE,WAAW;IACzB,eAAe;QACb,OAAO,WAAW,CAAA;IACpB,CAAC;IACD,qBAAqB;QACnB,OAAO,WAAW,CAAA;IACpB,CAAC;CACF,CAAA"}
1
+ {"version":3,"file":"analytics-track-outcome.js","sourceRoot":"","sources":["../../../../src/nodes/analytics/analytics-track-outcome.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,cAAc,CAAC,CAAA;AAEzD,MAAM,WAAW,GAAG;;;;;QAKZ,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;EAoB1C,CAAA;AAEF,MAAM,WAAW,GAAG;;;;;QAKZ,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0D1C,CAAA;AAEW,QAAA,qBAAqB,GAAyB;IACzD,QAAQ,EAAE,yBAAyB;IACnC,YAAY,EAAE,WAAW;IACzB,eAAe;QACb,OAAO,WAAW,CAAA;IACpB,CAAC;IACD,qBAAqB;QACnB,OAAO,WAAW,CAAA;IACpB,CAAC;CACF,CAAA"}