@m13v/seo-components 0.41.0 → 0.41.2
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/package.json
CHANGED
|
@@ -103,7 +103,24 @@ export function createDmShortLinkRedirectHandler(config: DmShortLinkRedirectConf
|
|
|
103
103
|
) {
|
|
104
104
|
const { code: rawCode } = await ctx.params;
|
|
105
105
|
const code = (rawCode || "").trim();
|
|
106
|
-
|
|
106
|
+
// Build the public home URL using forwarded headers. On Cloud Run the
|
|
107
|
+
// container is served at `0.0.0.0:8080` internally and Next.js sets
|
|
108
|
+
// `req.url` from that host, so `new URL("/", req.url)` would 302 users to
|
|
109
|
+
// `https://0.0.0.0:8080/` (unreachable). Vercel doesn't have this problem
|
|
110
|
+
// because its proxy rewrites `req.url` to the public host, but we share
|
|
111
|
+
// this handler across Vercel + Cloud Run consumers, so always prefer the
|
|
112
|
+
// forwarded host headers when present.
|
|
113
|
+
const fwdHost =
|
|
114
|
+
req.headers.get("x-forwarded-host") ||
|
|
115
|
+
req.headers.get("host") ||
|
|
116
|
+
"";
|
|
117
|
+
const fwdProto =
|
|
118
|
+
req.headers.get("x-forwarded-proto") ||
|
|
119
|
+
(fwdHost && !fwdHost.includes("localhost") ? "https" : "http");
|
|
120
|
+
const homeUrl =
|
|
121
|
+
fwdHost && !/(?:^|\.)0\.0\.0\.0(?::\d+)?$/.test(fwdHost)
|
|
122
|
+
? `${fwdProto}://${fwdHost}/`
|
|
123
|
+
: new URL("/", req.url).toString();
|
|
107
124
|
|
|
108
125
|
if (!/^[a-z0-9]{4,32}$/i.test(code)) {
|
|
109
126
|
return Response.redirect(homeUrl, 302);
|
|
@@ -126,6 +143,13 @@ export function createDmShortLinkRedirectHandler(config: DmShortLinkRedirectConf
|
|
|
126
143
|
let replyId: number | null = null;
|
|
127
144
|
let project: string | null = null;
|
|
128
145
|
let platform: string | null = null;
|
|
146
|
+
// Newsletter rail attribution: the resolver returns broadcast_id +
|
|
147
|
+
// broadcast_product + recipient_email_hash when the code resolves to
|
|
148
|
+
// newsletter_links. We fire `newsletter_short_link_clicked` so PostHog
|
|
149
|
+
// can stitch the click back to the originating broadcast + recipient.
|
|
150
|
+
let broadcastId: number | null = null;
|
|
151
|
+
let broadcastProduct: string | null = null;
|
|
152
|
+
let recipientEmailHash: string | null = null;
|
|
129
153
|
|
|
130
154
|
try {
|
|
131
155
|
const params = new URLSearchParams();
|
|
@@ -152,6 +176,9 @@ export function createDmShortLinkRedirectHandler(config: DmShortLinkRedirectConf
|
|
|
152
176
|
reply_id?: number;
|
|
153
177
|
project?: string;
|
|
154
178
|
platform?: string;
|
|
179
|
+
broadcast_id?: number;
|
|
180
|
+
broadcast_product?: string;
|
|
181
|
+
recipient_email_hash?: string;
|
|
155
182
|
};
|
|
156
183
|
if (body.target_url) {
|
|
157
184
|
target = body.target_url;
|
|
@@ -160,6 +187,9 @@ export function createDmShortLinkRedirectHandler(config: DmShortLinkRedirectConf
|
|
|
160
187
|
replyId = body.reply_id ?? null;
|
|
161
188
|
project = body.project ?? null;
|
|
162
189
|
platform = body.platform ?? null;
|
|
190
|
+
broadcastId = body.broadcast_id ?? null;
|
|
191
|
+
broadcastProduct = body.broadcast_product ?? null;
|
|
192
|
+
recipientEmailHash = body.recipient_email_hash ?? null;
|
|
163
193
|
}
|
|
164
194
|
}
|
|
165
195
|
} catch (err) {
|
|
@@ -231,6 +261,34 @@ export function createDmShortLinkRedirectHandler(config: DmShortLinkRedirectConf
|
|
|
231
261
|
);
|
|
232
262
|
}
|
|
233
263
|
|
|
264
|
+
// Newsletter rail event. distinct_id is the recipient_email_hash so
|
|
265
|
+
// every click from the same recipient stitches under one identity in
|
|
266
|
+
// PostHog. Properties carry broadcast_id + product so HogQL queries
|
|
267
|
+
// can attribute clicks (and downstream signup events on the same
|
|
268
|
+
// session) back to a specific broadcast x recipient pair.
|
|
269
|
+
if (!isBot && target && posthogKey && broadcastId != null) {
|
|
270
|
+
fetch(`${posthogHost}/i/v0/e/`, {
|
|
271
|
+
method: "POST",
|
|
272
|
+
headers: { "Content-Type": "application/json" },
|
|
273
|
+
body: JSON.stringify({
|
|
274
|
+
api_key: posthogKey,
|
|
275
|
+
event: "newsletter_short_link_clicked",
|
|
276
|
+
distinct_id: recipientEmailHash || `newsletter_${broadcastId}`,
|
|
277
|
+
timestamp: new Date().toISOString(),
|
|
278
|
+
properties: {
|
|
279
|
+
broadcast_id: broadcastId,
|
|
280
|
+
broadcast_product: broadcastProduct,
|
|
281
|
+
recipient_email_hash: recipientEmailHash,
|
|
282
|
+
project,
|
|
283
|
+
code,
|
|
284
|
+
site,
|
|
285
|
+
},
|
|
286
|
+
}),
|
|
287
|
+
}).catch((err) =>
|
|
288
|
+
console.error("[dm-short-link/redirect] posthog fetch failed:", err)
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
234
292
|
return Response.redirect(target || homeUrl, 302);
|
|
235
293
|
};
|
|
236
294
|
}
|
|
@@ -15,7 +15,7 @@ import { discoverGuides } from "./discover-guides";
|
|
|
15
15
|
import { logAiUsage } from "./ai-usage";
|
|
16
16
|
|
|
17
17
|
const MODEL_ID = "gemini-flash-latest";
|
|
18
|
-
const MAX_TOOL_ROUNDS =
|
|
18
|
+
const MAX_TOOL_ROUNDS = 50;
|
|
19
19
|
const RATE_LIMIT_MAX = 20;
|
|
20
20
|
const RATE_LIMIT_WINDOW_MS = 60 * 60 * 1000;
|
|
21
21
|
|