lytx 0.3.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.
Files changed (213) hide show
  1. package/.env.example +37 -0
  2. package/README.md +486 -0
  3. package/alchemy.run.ts +155 -0
  4. package/cli/bootstrap-admin.ts +284 -0
  5. package/cli/deploy-staging.ts +692 -0
  6. package/cli/import-events.ts +628 -0
  7. package/cli/import-sites.ts +518 -0
  8. package/cli/index.ts +609 -0
  9. package/cli/init-db.ts +269 -0
  10. package/cli/migrate-to-durable-objects.ts +564 -0
  11. package/cli/migration-worker.ts +300 -0
  12. package/cli/performance-test.ts +588 -0
  13. package/cli/pg/client.ts +4 -0
  14. package/cli/pg/new-site.ts +153 -0
  15. package/cli/rollback-durable-objects.ts +622 -0
  16. package/cli/seed-data.ts +459 -0
  17. package/cli/setup.js +18 -0
  18. package/cli/setup.ts +463 -0
  19. package/cli/validate-migration.ts +200 -0
  20. package/cli/wrangler-migration.jsonc +28 -0
  21. package/db/adapter.ts +166 -0
  22. package/db/analytics_engine/client.ts +0 -0
  23. package/db/analytics_engine/sites.ts +0 -0
  24. package/db/client.ts +16 -0
  25. package/db/d1/client.ts +8 -0
  26. package/db/d1/drizzle.config.ts +35 -0
  27. package/db/d1/migrations/0000_true_maelstrom.sql +165 -0
  28. package/db/d1/migrations/0001_wonderful_bloodaxe.sql +12 -0
  29. package/db/d1/migrations/0002_late_frightful_four.sql +1 -0
  30. package/db/d1/migrations/0003_cuddly_obadiah_stane.sql +16 -0
  31. package/db/d1/migrations/0004_mute_stardust.sql +1 -0
  32. package/db/d1/migrations/0005_awesome_silvermane.sql +3 -0
  33. package/db/d1/migrations/0006_volatile_shriek.sql +2 -0
  34. package/db/d1/migrations/0007_superb_lila_cheney.sql +1 -0
  35. package/db/d1/migrations/0008_bitter_longshot.sql +17 -0
  36. package/db/d1/migrations/0009_wonderful_madame_masque.sql +28 -0
  37. package/db/d1/migrations/meta/0000_snapshot.json +1112 -0
  38. package/db/d1/migrations/meta/0001_snapshot.json +1187 -0
  39. package/db/d1/migrations/meta/0002_snapshot.json +1194 -0
  40. package/db/d1/migrations/meta/0003_snapshot.json +1296 -0
  41. package/db/d1/migrations/meta/0004_snapshot.json +1303 -0
  42. package/db/d1/migrations/meta/0005_snapshot.json +1325 -0
  43. package/db/d1/migrations/meta/0006_snapshot.json +1339 -0
  44. package/db/d1/migrations/meta/0007_snapshot.json +1347 -0
  45. package/db/d1/migrations/meta/0008_snapshot.json +1464 -0
  46. package/db/d1/migrations/meta/0009_snapshot.json +1648 -0
  47. package/db/d1/migrations/meta/_journal.json +76 -0
  48. package/db/d1/schema.ts +407 -0
  49. package/db/d1/sites.ts +374 -0
  50. package/db/d1/teamAiUsage.ts +101 -0
  51. package/db/d1/teams.ts +127 -0
  52. package/db/durable/drizzle.config.ts +8 -0
  53. package/db/durable/durableObjectClient.ts +480 -0
  54. package/db/durable/events.ts +100 -0
  55. package/db/durable/migrations/0000_fair_bucky.sql +38 -0
  56. package/db/durable/migrations/meta/0000_snapshot.json +278 -0
  57. package/db/durable/migrations/meta/_journal.json +13 -0
  58. package/db/durable/migrations/migrations.js +10 -0
  59. package/db/durable/schema.ts +5 -0
  60. package/db/durable/siteDurableObject.ts +1352 -0
  61. package/db/durable/types.ts +53 -0
  62. package/db/postgres/client.ts +13 -0
  63. package/db/postgres/drizzle.config.ts +12 -0
  64. package/db/postgres/migrations/0000_brainy_sprite.sql +116 -0
  65. package/db/postgres/migrations/meta/0000_snapshot.json +681 -0
  66. package/db/postgres/migrations/meta/_journal.json +13 -0
  67. package/db/postgres/schema.ts +145 -0
  68. package/db/postgres/sites.ts +118 -0
  69. package/db/tranformReports.ts +595 -0
  70. package/db/types.ts +55 -0
  71. package/endpoints/api_worker.tsx +1854 -0
  72. package/endpoints/site_do_worker.ts +11 -0
  73. package/index.d.ts +63 -0
  74. package/index.ts +83 -0
  75. package/lib/auth.ts +279 -0
  76. package/lib/geojson/world_countries.json +45307 -0
  77. package/lib/random_name.ts +41 -0
  78. package/lib/sendMail.ts +252 -0
  79. package/package.json +142 -0
  80. package/public/favicon.ico +0 -0
  81. package/public/images/android-chrome-192x192.png +0 -0
  82. package/public/images/android-chrome-512x512.png +0 -0
  83. package/public/images/apple-touch-icon.png +0 -0
  84. package/public/images/favicon-16x16.png +0 -0
  85. package/public/images/favicon-32x32.png +0 -0
  86. package/public/images/lytx_dark_dashboard.png +0 -0
  87. package/public/images/lytx_light_dashboard.png +0 -0
  88. package/public/images/safari-pinned-tab.svg +4 -0
  89. package/public/logo.png +0 -0
  90. package/public/site.webmanifest +26 -0
  91. package/public/sw.js +107 -0
  92. package/src/Document.tsx +86 -0
  93. package/src/api/ai_api.ts +1156 -0
  94. package/src/api/authMiddleware.ts +45 -0
  95. package/src/api/auth_api.ts +465 -0
  96. package/src/api/event_labels_api.ts +193 -0
  97. package/src/api/events_api.ts +210 -0
  98. package/src/api/queueWorker.ts +303 -0
  99. package/src/api/reports_api.ts +278 -0
  100. package/src/api/seed_api.ts +288 -0
  101. package/src/api/sites_api.ts +904 -0
  102. package/src/api/tag_api.ts +458 -0
  103. package/src/api/tag_api_v2.ts +289 -0
  104. package/src/api/team_api.ts +456 -0
  105. package/src/app/Dashboard.tsx +1339 -0
  106. package/src/app/Events.tsx +974 -0
  107. package/src/app/Explore.tsx +312 -0
  108. package/src/app/Layout.tsx +58 -0
  109. package/src/app/Settings.tsx +1302 -0
  110. package/src/app/components/DashboardCard.tsx +118 -0
  111. package/src/app/components/EditableCell.tsx +123 -0
  112. package/src/app/components/EventForm.tsx +93 -0
  113. package/src/app/components/MarketingFooter.tsx +49 -0
  114. package/src/app/components/MarketingNav.tsx +150 -0
  115. package/src/app/components/Nav.tsx +755 -0
  116. package/src/app/components/NewSiteSetup.tsx +298 -0
  117. package/src/app/components/SQLEditor.tsx +740 -0
  118. package/src/app/components/SiteSelector.tsx +126 -0
  119. package/src/app/components/SiteTag.tsx +42 -0
  120. package/src/app/components/SiteTagInstallCard.tsx +241 -0
  121. package/src/app/components/WorldMapCard.tsx +337 -0
  122. package/src/app/components/charts/ChartComponents.tsx +1481 -0
  123. package/src/app/components/charts/EventFunnel.tsx +45 -0
  124. package/src/app/components/charts/EventSummary.tsx +194 -0
  125. package/src/app/components/charts/SankeyFlows.tsx +72 -0
  126. package/src/app/components/marketing/CheckIcon.tsx +16 -0
  127. package/src/app/components/marketing/MarketingLayout.tsx +23 -0
  128. package/src/app/components/marketing/SectionHeading.tsx +35 -0
  129. package/src/app/components/reports/AskAiWorkspace.tsx +371 -0
  130. package/src/app/components/reports/CreateReportStarter.tsx +74 -0
  131. package/src/app/components/reports/DashboardRouteFiltersContext.tsx +14 -0
  132. package/src/app/components/reports/DashboardToolbar.tsx +154 -0
  133. package/src/app/components/reports/DashboardWorkspaceLayout.tsx +63 -0
  134. package/src/app/components/reports/DashboardWorkspaceShell.tsx +118 -0
  135. package/src/app/components/reports/ReportBuilderWorkspace.tsx +76 -0
  136. package/src/app/components/reports/custom/CustomReportBuilderPage.tsx +1667 -0
  137. package/src/app/components/reports/custom/ReportWidgetChart.tsx +297 -0
  138. package/src/app/components/reports/custom/buildWidgetSql.ts +151 -0
  139. package/src/app/components/reports/custom/chartPalettes.ts +18 -0
  140. package/src/app/components/reports/custom/types.ts +50 -0
  141. package/src/app/components/reports/reportBuilderMenuItems.ts +17 -0
  142. package/src/app/components/reports/useDashboardToolbarControls.tsx +235 -0
  143. package/src/app/components/ui/AlertBanner.tsx +101 -0
  144. package/src/app/components/ui/Button.tsx +55 -0
  145. package/src/app/components/ui/Card.tsx +80 -0
  146. package/src/app/components/ui/Input.tsx +72 -0
  147. package/src/app/components/ui/Link.tsx +23 -0
  148. package/src/app/components/ui/ReportBuilderMenu.tsx +246 -0
  149. package/src/app/components/ui/ThemeToggle.tsx +54 -0
  150. package/src/app/constants.ts +6 -0
  151. package/src/app/headers.ts +33 -0
  152. package/src/app/providers/AuthProvider.tsx +189 -0
  153. package/src/app/providers/ClientProviders.tsx +18 -0
  154. package/src/app/providers/QueryProvider.tsx +23 -0
  155. package/src/app/providers/ThemeProvider.tsx +88 -0
  156. package/src/app/utils/chartThemes.ts +146 -0
  157. package/src/app/utils/keybinds.ts +96 -0
  158. package/src/app/utils/media.tsx +24 -0
  159. package/src/client.tsx +114 -0
  160. package/src/config/createLytxAppConfig.ts +252 -0
  161. package/src/config/resourceNames.ts +88 -0
  162. package/src/db/index.ts +67 -0
  163. package/src/index.css +285 -0
  164. package/src/lib/featureFlags.ts +69 -0
  165. package/src/pages/GetStarted.tsx +290 -0
  166. package/src/pages/Home.tsx +268 -0
  167. package/src/pages/Login.tsx +283 -0
  168. package/src/pages/PrivacyPolicy.tsx +120 -0
  169. package/src/pages/Signup.tsx +267 -0
  170. package/src/pages/TermsOfService.tsx +126 -0
  171. package/src/pages/VerifyEmail.tsx +56 -0
  172. package/src/session/durableObject.ts +7 -0
  173. package/src/session/siteSchema.ts +86 -0
  174. package/src/session/types.ts +36 -0
  175. package/src/templates/README.md +80 -0
  176. package/src/templates/cleanFunctions.js +44 -0
  177. package/src/templates/embedFunctions.js +52 -0
  178. package/src/templates/lytx-shared.ts +662 -0
  179. package/src/templates/lytxpixel-core.ts +144 -0
  180. package/src/templates/lytxpixel.ts +267 -0
  181. package/src/templates/lytxpixelBrowser.js +634 -0
  182. package/src/templates/lytxpixelBrowser.mjs +634 -0
  183. package/src/templates/parseData.js +12 -0
  184. package/src/templates/script.ts +31 -0
  185. package/src/templates/template.tsx +50 -0
  186. package/src/templates/test.js +3 -0
  187. package/src/templates/trackWebEvents.ts +177 -0
  188. package/src/templates/vendors/clickcease.ts +8 -0
  189. package/src/templates/vendors/google.ts +174 -0
  190. package/src/templates/vendors/linkedin.ts +23 -0
  191. package/src/templates/vendors/meta.ts +56 -0
  192. package/src/templates/vendors/quantcast.ts +22 -0
  193. package/src/templates/vendors/simplfi.ts +7 -0
  194. package/src/types/app-context.ts +16 -0
  195. package/src/utilities/dashboardParams.ts +188 -0
  196. package/src/utilities/dashboardQueries.ts +537 -0
  197. package/src/utilities/dashboardTransforms.ts +167 -0
  198. package/src/utilities/dataValidation.ts +414 -0
  199. package/src/utilities/detector.ts +73 -0
  200. package/src/utilities/encrypt.ts +103 -0
  201. package/src/utilities/index.ts +13 -0
  202. package/src/utilities/parser.ts +117 -0
  203. package/src/utilities/performanceMonitoring.ts +570 -0
  204. package/src/utilities/route_interuptors.ts +24 -0
  205. package/src/worker.tsx +675 -0
  206. package/tsconfig.json +78 -0
  207. package/types/env.d.ts +16 -0
  208. package/types/rw.d.ts +7 -0
  209. package/types/shims.d.ts +53 -0
  210. package/types/vite.d.ts +19 -0
  211. package/vite/vite-plugin-pixel-bundle.ts +126 -0
  212. package/vite.config.ts +53 -0
  213. package/worker-configuration.d.ts +8401 -0
@@ -0,0 +1,289 @@
1
+ import { route } from "rwsdk/router";
2
+ import type { RequestInfo } from "rwsdk/worker";
3
+ import { env } from "cloudflare:workers";
4
+ import type { PageEvent } from "@/templates/lytxpixel";
5
+ import { script_tag_manager, script_core } from "virtual:lytx-pixel-raw";
6
+ // import { getSiteForTag as getSiteForTagPg } from "@db/postgres/sites";
7
+ import { getSiteForTag as getSiteForTagD1, rotateSiteRidSalt } from "@db/d1/sites";
8
+ import { insertSiteEvents } from "@db/adapter";
9
+ import { enqueueSiteEventsForProcessing } from "@/api/queueWorker";
10
+ import { blockedQueryParams, WebEvent } from "@/templates/trackWebEvents";
11
+ import { parseBrowser, parseOs, parseDeviceType, parseUserAgent } from "@/utilities/detector";
12
+ import { hashIpAddress } from "@/utilities";
13
+ import type { DBAdapter } from "@db/types";
14
+ import { IS_DEV } from "rwsdk/constants";
15
+ import { SiteEventInput } from "@/session/siteSchema";
16
+
17
+ export const dataVariableName = "lytxDataLayer" as const;
18
+
19
+ export function corsMiddleware({ response }: RequestInfo) {
20
+ const headers = response.headers;
21
+ headers.set("Access-Control-Allow-Origin", "*");
22
+ headers.set("Access-Control-Allow-Methods", "POST, OPTIONS, GET");
23
+ headers.set("Access-Control-Allow-Headers", "X-Custom-Header, Upgrade-Insecure-Requests, Content-Type");
24
+ headers.set("Access-Control-Expose-Headers", "Content-Length, X-Kuma-Revision");
25
+ headers.set("Access-Control-Max-Age", "600");
26
+ headers.set("Access-Control-Allow-Credentials", "false");
27
+ }
28
+ //TODO: move to seprate
29
+ function checkIfTagManager(events: PageEvent[], allowed = false) {
30
+ const eventsMapped = [];
31
+ for (const ev of events) {
32
+ if (allowed) {
33
+ eventsMapped.push({
34
+ event_name: ev.event_name,
35
+ QuantcastPixelId: ev.QuantcastPixelId,
36
+ QuantCastPixelLabel: ev.QuantCastPixelLabel,
37
+ SimplfiPixelid: ev.SimplfiPixelid,
38
+ googleanalytics: ev.googleanalytics,
39
+ googleadsscript: ev.googleadsscript,
40
+ googleadsconversion: ev.googleadsconversion,
41
+ metaEvent: ev.metaEvent,
42
+ linkedinEvent: ev.linkedinEvent,
43
+ clickCease: ev.clickCease,
44
+ condition: ev.condition,
45
+ data_passback: ev.data_passback,
46
+ parameters: ev.parameters,
47
+ paramConfig: ev.paramConfig,
48
+ query_parameters: ev.query_parameters,
49
+ customScript: ev.customScript,
50
+ rules: ev.rules,
51
+ Notes: ev.Notes,
52
+ })
53
+ } else {
54
+ eventsMapped.push({
55
+ event_name: ev.event_name,
56
+ condition: ev.condition,
57
+ data_passback: ev.data_passback,
58
+ parameters: ev.parameters,
59
+ paramConfig: ev.paramConfig,
60
+ query_parameters: ev.query_parameters,
61
+ rules: ev.rules,
62
+ Notes: ev.Notes,
63
+ })
64
+ }
65
+ }
66
+ return eventsMapped
67
+
68
+ }
69
+
70
+ export const lytxTag = (adapter: DBAdapter, route_path = "/lytx.v2.js") => route(route_path, [corsMiddleware, async ({ request }) => {
71
+ if (request.method !== "GET") return new Response("Not Found.", { status: 404 });
72
+ const url = new URL(request.url);
73
+ const lytxDomain = url.origin;
74
+
75
+ const accountRaw = url.searchParams.get("account");
76
+ const account = typeof accountRaw === "string" ? accountRaw : undefined;
77
+ const platformName = "web" as const;
78
+
79
+ const macros = Array.from(url.searchParams.entries())
80
+ .filter(([k]) => k !== "account" && k !== "platform")
81
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
82
+ .join("&");
83
+
84
+ let events: PageEvent[] | null = null;
85
+ const config = {
86
+ site: "",
87
+ tag: "",
88
+ track_web_events: false,
89
+ tag_manager: false,
90
+ gdpr: false,
91
+ autocapture: false,
92
+ event_load_strategy: "sdk" as "sdk" | "kv",
93
+ };
94
+
95
+ if (account) {
96
+ const checkAccouuntByTagId = await getSiteForTagD1(account as string);
97
+
98
+ let accountKey: string | undefined;
99
+ if (checkAccouuntByTagId) {
100
+ accountKey = (checkAccouuntByTagId.tag_id_override ?? checkAccouuntByTagId.tag_id) || undefined;
101
+ config.site = checkAccouuntByTagId.domain ?? "";
102
+ config.tag = checkAccouuntByTagId.tag_id as string;
103
+ config.track_web_events = checkAccouuntByTagId.track_web_events;
104
+ config.tag_manager = checkAccouuntByTagId.tag_manager ?? false;
105
+ config.gdpr = checkAccouuntByTagId.gdpr ?? false;
106
+ config.autocapture = checkAccouuntByTagId.autocapture ?? false;
107
+ config.event_load_strategy = (checkAccouuntByTagId.event_load_strategy ?? "sdk") as "sdk" | "kv";
108
+ } else {
109
+ //TODO: Save this in a kv so i can check later for legacy sites
110
+ if (IS_DEV) console.log("🔥🔥🔥 No account found for ", account);
111
+ }
112
+ if (accountKey && config.event_load_strategy !== "sdk") {
113
+ const checkKey = (await env.LYTX_EVENTS.get(accountKey, { type: "json" })) as unknown as PageEvent[];
114
+ events = checkKey ? checkKey : [];
115
+ } else {
116
+ events = [];
117
+ }
118
+ }
119
+ ///trackWebEvent.v2
120
+ const lytx_script = (lytxDomain: string) => {
121
+ // Use core bundle (no vendors) when tag_manager is disabled, full bundle otherwise
122
+ const baseScript = config.tag_manager ? script_tag_manager : script_core;
123
+ // Replace the placeholder with the actual domain (includes protocol)
124
+ const domain = IS_DEV ? "http://localhost:6123" : lytxDomain;
125
+ return baseScript.replace("__LYTX_DOMAIN__", domain);
126
+ }
127
+ if (!events) return new Response("Not Found.", { status: 404 });
128
+ const eventsOut = checkIfTagManager(events, config.tag_manager);
129
+ const shouldLoadEvents = config.event_load_strategy !== "sdk";
130
+ const safeEvents = shouldLoadEvents ? eventsOut : [];
131
+
132
+ const siteCfg = { site: config.site, tag: config.tag, autocapture: config.autocapture };
133
+ const script = `//! Copyright ${new Date().getFullYear()} Lytx All rights reserved.
134
+ (function () {
135
+ const ${dataVariableName} = ${JSON.stringify(safeEvents)};
136
+ if (window.${dataVariableName}) {
137
+ window.${dataVariableName}.push({site:${JSON.stringify(config.site)},tag:${JSON.stringify(config.tag)},events:${dataVariableName},tracked:[]});
138
+ } else {
139
+ window.${dataVariableName} = [{site:${JSON.stringify(config.site)},tag:${JSON.stringify(config.tag)},events:${dataVariableName},tracked:[]}];
140
+ }
141
+ ${lytx_script(lytxDomain)}
142
+ parseData(${dataVariableName}, ${JSON.stringify(siteCfg)}, ${JSON.stringify(!!config.track_web_events)}, "web");
143
+ ${config.track_web_events ? `trackEvents(${JSON.stringify(config.tag)}, "web", null, ${JSON.stringify(macros)});` : ``}
144
+ })();`;
145
+
146
+ return new Response(script, { headers: { "Content-Type": "text/javascript" } });
147
+ }]);
148
+
149
+ type TrackWebEventOptions = {
150
+ useQueue?: boolean;
151
+ };
152
+
153
+ export const trackWebEvent = (
154
+ adapter: DBAdapter,
155
+ route_path = "/trackWebEvent.v2",
156
+ options: TrackWebEventOptions = {},
157
+ ) => route(route_path, [corsMiddleware, async ({ request }) => {
158
+ // Handle CORS preflight
159
+ if (request.method === "OPTIONS") {
160
+ return new Response(null, { status: 204 });
161
+ }
162
+ if (request.method !== "POST") return new Response("Not Found.", { status: 404 });
163
+ const url = new URL(request.url);
164
+
165
+ const account = url.searchParams.get("account");
166
+ const data = (await request.json()) as {
167
+ referer: string;
168
+ event: WebEvent["event"] | Record<"custom", string>;
169
+ client_page_url: string;
170
+ screen_width: number;
171
+ screen_height: number;
172
+ browser?: string;
173
+ operating_system?: string;
174
+ rid?: string;
175
+ device_type?: string;
176
+ custom_data?: Record<string, string>;
177
+ };
178
+
179
+ if (!account) return new Response("Not Found.", { status: 404 });
180
+
181
+ // Use D1 for admin/site info (source of truth)
182
+ const site = await getSiteForTagD1(account);
183
+ if (!site || !site.track_web_events) return new Response("Not Found.", { status: 404 });
184
+ const siteAdapter = (site.site_db_adapter ?? adapter) as DBAdapter;
185
+ const shouldUseQueue = options.useQueue ?? false;
186
+
187
+ const eventName = (typeof data.event === "object" ? data.event.custom : (data.event ?? "page_view")) as string;
188
+ const isRuleDefinitionEvent = eventName === "auto_capture" && data.custom_data?.type === "auto_capture";
189
+ if (isRuleDefinitionEvent) {
190
+ if (IS_DEV) console.log("🔥🔥🔥 Skipping auto_capture rule definition event");
191
+ return new Response(JSON.stringify({ error: null, status: 200, rid: null, queued: false, skipped: true }), {
192
+ headers: { "Content-Type": "application/json" }
193
+ });
194
+ }
195
+
196
+ // Build query params safely (web only)
197
+ const clientUrl = new URL(data.client_page_url);
198
+ const queryParams: Record<string, string> = {};
199
+
200
+ //PERF: check if this is the best way to do this later
201
+ clientUrl.searchParams.forEach((value: string, key: string) => {
202
+ if (!blockedQueryParams.find((q) => q.includes(key.toLowerCase()))) {
203
+ queryParams[key] = value;
204
+ }
205
+ });
206
+
207
+ const ua = request.headers.get("User-Agent") || "";
208
+ const parsedUA = parseUserAgent(ua);
209
+
210
+ const siteEvent: SiteEventInput = {
211
+ page_url: request.headers.get("referer") ?? "Unknown",
212
+ referer: data.referer,
213
+ screen_height: data.screen_height,
214
+ screen_width: data.screen_width,
215
+ client_page_url: typeof clientUrl !== "string" ? clientUrl.pathname : "",
216
+ browser: parseBrowser(parsedUA, ua),
217
+ operating_system: parseOs(parsedUA, request.headers.get("sec-ch-ua-platform") || ""),
218
+ device_type: parseDeviceType(parsedUA, request.headers.get("sec-ch-ua-mobile") || ""),
219
+ // bot_data: parsedUA.bot as any,
220
+ country: (request.cf as IncomingRequestCfProperties | undefined)?.country ?? undefined,
221
+ region: (request.cf as IncomingRequestCfProperties | undefined)?.region ?? undefined,
222
+ city: (request.cf as IncomingRequestCfProperties | undefined)?.city ?? undefined,
223
+ postal: (request.cf as IncomingRequestCfProperties | undefined)?.postalCode ?? undefined,
224
+ event: eventName as string,
225
+ tag_id: account,
226
+ query_params: queryParams,
227
+ custom_data: data.custom_data,
228
+ };
229
+
230
+ let rid: string | null = null;
231
+ if (site.gdpr) {
232
+ rid = data.rid ?? null;
233
+ } else {
234
+ const rawExpire = site.rid_salt_expire ? new Date(site.rid_salt_expire) : null;
235
+ const isExpired = !rawExpire || Number.isNaN(rawExpire.getTime()) || rawExpire <= new Date();
236
+ const rotated = isExpired ? await rotateSiteRidSalt(site.site_id) : null;
237
+ const ridSalt = rotated?.rid_salt ?? site.rid_salt ?? null;
238
+ const ipAddress =
239
+ request.headers.get("cf-connecting-ip") ||
240
+ request.headers.get("x-real-ip") ||
241
+ request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
242
+ "";
243
+ if (ipAddress && ridSalt) {
244
+ rid = await hashIpAddress(ipAddress, ridSalt);
245
+ } else if (data.rid) {
246
+ rid = data.rid;
247
+ }
248
+ }
249
+ if (rid) {
250
+ siteEvent.rid = rid;
251
+ }
252
+
253
+ if (shouldUseQueue) {
254
+ try {
255
+ if (IS_DEV) console.log("🔥🔥🔥 Enqueueing site event for processing");
256
+ await enqueueSiteEventsForProcessing({
257
+ siteId: site.site_id,
258
+ siteUuid: site.uuid,
259
+ teamId: site.team_id,
260
+ adapter: siteAdapter,
261
+ events: [siteEvent],
262
+ });
263
+ } catch (error) {
264
+ console.error("🔥🔥🔥 Queue enqueue failed", { error: error instanceof Error ? error.message : "queue enqueue failed" });
265
+ return new Response(JSON.stringify({
266
+ error: error instanceof Error ? error.message : "queue enqueue failed",
267
+ status: 500,
268
+ }), {
269
+ headers: { "Content-Type": "application/json" },
270
+ status: 500,
271
+ });
272
+ }
273
+
274
+ return new Response(JSON.stringify({ error: null, status: 200, rid, queued: true }), {
275
+ headers: { "Content-Type": "application/json" }
276
+ });
277
+ }
278
+
279
+ const result = await insertSiteEvents(site.site_id, site.uuid, [siteEvent], siteAdapter);
280
+ if (!result.success) {
281
+ return new Response(JSON.stringify({ error: result.error ?? "insert failed", status: 500 }), {
282
+ headers: { "Content-Type": "application/json" }, status: 500
283
+ });
284
+ }
285
+
286
+ return new Response(JSON.stringify({ error: null, status: 200, rid, queued: false }), {
287
+ headers: { "Content-Type": "application/json" }
288
+ });
289
+ }]);