From 7d3761d5e625eed65506e7058ede92c5d4e87224 Mon Sep 17 00:00:00 2001 From: KartikLabhshetwar Date: Thu, 5 Feb 2026 12:41:02 +0530 Subject: [PATCH 1/6] fix: consoldating two ad tracking into one --- lib/hooks/use-gravity-ad.ts | 39 ++++++------- server/index.ts | 2 - server/routes/adtrack.ts | 70 ----------------------- server/routes/gravity.ts | 111 ++++++++++++++++++++++++------------ 4 files changed, 90 insertions(+), 132 deletions(-) delete mode 100644 server/routes/adtrack.ts diff --git a/lib/hooks/use-gravity-ad.ts b/lib/hooks/use-gravity-ad.ts index 6c0e001c..f184e437 100644 --- a/lib/hooks/use-gravity-ad.ts +++ b/lib/hooks/use-gravity-ad.ts @@ -292,10 +292,11 @@ export function useGravityAd({ placeholderData: (prev) => prev, }); - // Stable tracking function - memoized to prevent re-renders - const sendTrackingEvent = useCallback( - (type: "impression" | "click" | "dismiss", ad: ContextAd | null) => { - if (!ad || !sessionId) return; + // Shared helper to send ad events to /api/px + // All ad tracking goes through this single endpoint + const sendAdEvent = useCallback( + (type: "impression" | "click" | "dismiss", ad: ContextAd) => { + if (!sessionId) return; let hostname = ""; try { @@ -304,6 +305,11 @@ export function useGravityAd({ // Ignore invalid URLs } + // For impressions, include the Gravity URL for billing; clicks/dismisses don't need it + const trackUrl = type === "impression" + ? getApiUrl(`/api/px?url=${encodeURIComponent(ad.impUrl)}`) + : getApiUrl("/api/px"); + const payload = JSON.stringify({ type, sessionId, @@ -320,8 +326,6 @@ export function useGravityAd({ browser: deviceInfo?.browser, }); - const trackUrl = getApiUrl("/api/adtrack"); - // Use sendBeacon for reliable non-blocking tracking if (typeof navigator !== "undefined" && navigator.sendBeacon) { navigator.sendBeacon(trackUrl, new Blob([payload], { type: "application/json" })); @@ -341,36 +345,25 @@ export function useGravityAd({ const fireImpression = useCallback( (targetAd?: ContextAd) => { const ad = targetAd ?? query.data?.[0]; - if (!ad) return; - - // Fire impression to Gravity via proxy (for billing) - const proxyUrl = getApiUrl(`/api/px?url=${encodeURIComponent(ad.impUrl)}`); - if (typeof navigator !== "undefined" && navigator.sendBeacon) { - navigator.sendBeacon(proxyUrl); - } else { - fetch(proxyUrl, { method: "POST", keepalive: true }).catch(() => {}); - } - - // Track locally for analytics - sendTrackingEvent("impression", ad); + if (ad) sendAdEvent("impression", ad); }, - [query.data, sendTrackingEvent] + [query.data, sendAdEvent] ); const fireClick = useCallback( (targetAd?: ContextAd) => { const ad = targetAd ?? query.data?.[0]; - sendTrackingEvent("click", ad ?? null); + if (ad) sendAdEvent("click", ad); }, - [query.data, sendTrackingEvent] + [query.data, sendAdEvent] ); const fireDismiss = useCallback( (targetAd?: ContextAd) => { const ad = targetAd ?? query.data?.[0]; - sendTrackingEvent("dismiss", ad ?? null); + if (ad) sendAdEvent("dismiss", ad); }, - [query.data, sendTrackingEvent] + [query.data, sendAdEvent] ); // Memoize the result to prevent object recreation diff --git a/server/index.ts b/server/index.ts index c4d6fed0..cc7177e6 100644 --- a/server/index.ts +++ b/server/index.ts @@ -13,7 +13,6 @@ import { chatHistoryRoutes } from "./routes/chat-history"; import { webhookRoutes } from "./routes/webhooks"; import { bypassDetectionRoutes } from "./routes/bypass-detection"; import { gravityRoutes } from "./routes/gravity"; -import { adtrackRoutes } from "./routes/adtrack"; import { startMemoryMonitor, getCurrentMemory } from "../lib/memory-monitor"; import { checkErrorRateAndAlert } from "../lib/alerting"; import { env } from "./env"; @@ -74,7 +73,6 @@ const app = new Elysia({ adapter: node() }) .use(webhookRoutes) .use(bypassDetectionRoutes) .use(gravityRoutes) - .use(adtrackRoutes) .onError(({ code, error, set, request }) => { // Don't log 404s for common browser requests (favicon, etc) if (code === "NOT_FOUND") { diff --git a/server/routes/adtrack.ts b/server/routes/adtrack.ts deleted file mode 100644 index 6faac836..00000000 --- a/server/routes/adtrack.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Ad Tracking Routes - POST /api/adtrack - * - * Lightweight endpoint for client-side ad event tracking (impressions, clicks, dismissals). - * Uses sendBeacon on client for reliable tracking during navigation. - * All tracking is non-blocking and fire-and-forget. - */ - -import { Elysia, t } from "elysia"; -import { trackAdEvent, type AdEventType } from "../../lib/clickhouse"; -import { createLogger } from "../../lib/logger"; - -const logger = createLogger("api:adtrack"); - -export const adtrackRoutes = new Elysia({ prefix: "/api" }).post( - "/adtrack", - async ({ body, set }) => { - const { type, sessionId, hostname, brandName, adTitle, adText, clickUrl, impUrl, cta, favicon, deviceType, os, browser } = body; - - // Fire-and-forget tracking - don't block the response - try { - trackAdEvent({ - event_type: type as AdEventType, - session_id: sessionId, - hostname, - brand_name: brandName, - ad_title: adTitle, - ad_text: adText, - click_url: clickUrl, - imp_url: impUrl, - cta, - favicon, - device_type: deviceType, - os, - browser, - status: "filled", // Client-side events only fire for filled ads - }); - - logger.debug({ type, hostname, brandName }, "Ad event tracked"); - } catch (error) { - // Log but don't fail the request - tracking is best-effort - logger.warn({ error: String(error), type }, "Failed to track ad event"); - } - - // Return 204 No Content immediately - client doesn't need a response body - set.status = 204; - return; - }, - { - body: t.Object({ - type: t.Union([ - t.Literal("impression"), - t.Literal("click"), - t.Literal("dismiss"), - ]), - sessionId: t.String(), - hostname: t.String(), - brandName: t.Optional(t.String()), - adTitle: t.Optional(t.String()), - adText: t.Optional(t.String()), - clickUrl: t.Optional(t.String()), - impUrl: t.Optional(t.String()), - cta: t.Optional(t.String()), - favicon: t.Optional(t.String()), - deviceType: t.Optional(t.String()), - os: t.Optional(t.String()), - browser: t.Optional(t.String()), - }), - } -); diff --git a/server/routes/gravity.ts b/server/routes/gravity.ts index a45915f3..171ee714 100644 --- a/server/routes/gravity.ts +++ b/server/routes/gravity.ts @@ -1,8 +1,11 @@ /** - * Gravity Context Routes - POST /api/context, POST /api/px + * Gravity Routes - POST /api/context, POST /api/px + * + * /api/context - Fetches contextual ads from Gravity AI for free users + * /api/px - Unified ad event tracking (impression, click, dismiss) + * - For impressions: forwards to Gravity for billing + tracks locally + * - For clicks/dismisses: tracks locally only * - * Fetches contextual content from Gravity AI for free users. - * Passes device/user info for better targeting. * Endpoint names are neutral to avoid content blockers. */ @@ -76,50 +79,84 @@ export interface GravityAdResponse { export const gravityRoutes = new Elysia({ prefix: "/api" }) .post( "/px", - async ({ query, set }) => { - const url = query.url; - - if (!url) { - set.status = 400; - return { error: "Missing url parameter" }; - } - - // Validate URL is from Gravity - try { - const parsedUrl = new URL(url); - if (!parsedUrl.hostname.endsWith("trygravity.ai")) { + async ({ query, body, set }) => { + const impUrl = query.url; + const eventType = body.type || "impression"; + + // For impressions, forward to Gravity for billing + if (eventType === "impression" && impUrl) { + // Validate URL is from Gravity + try { + const parsedUrl = new URL(impUrl); + if (!parsedUrl.hostname.endsWith("trygravity.ai")) { + set.status = 400; + return { error: "Invalid impression URL" }; + } + } catch { set.status = 400; - return { error: "Invalid impression URL" }; + return { error: "Invalid URL format" }; } - } catch { - set.status = 400; - return { error: "Invalid URL format" }; - } - // Forward the impression request to Gravity - try { - logger.info({ impUrl: url }, "Forwarding impression to Gravity"); - const response = await fetch(url, { - method: "GET", - headers: { - "User-Agent": "13ft-impression-proxy/1.0", - }, - }); - // Consume body to release connection resources - await response.text().catch(() => {}); - logger.info({ impUrl: url, status: response.status }, "Impression forwarded successfully"); - } catch (error) { - logger.warn({ impUrl: url, error: String(error) }, "Failed to forward impression"); - // Silently fail - impression tracking is best-effort + // Forward the impression request to Gravity + try { + logger.info({ impUrl }, "Forwarding impression to Gravity"); + const response = await fetch(impUrl, { + method: "GET", + headers: { + "User-Agent": "13ft-impression-proxy/1.0", + }, + }); + await response.text().catch(() => {}); + logger.info({ impUrl, status: response.status }, "Impression forwarded successfully"); + } catch (error) { + logger.warn({ impUrl, error: String(error) }, "Failed to forward impression"); + } } - // Return 204 No Content - the client doesn't need a response + // Track event locally for analytics + trackAdEvent({ + event_type: eventType as "impression" | "click" | "dismiss", + session_id: body.sessionId, + hostname: body.hostname || "", + brand_name: body.brandName || "", + ad_title: body.adTitle || "", + ad_text: body.adText || "", + click_url: body.clickUrl || "", + imp_url: body.impUrl || impUrl || "", + cta: body.cta || "", + favicon: body.favicon || "", + device_type: body.deviceType || "", + os: body.os || "", + browser: body.browser || "", + status: "filled", + }); + logger.debug({ type: eventType, hostname: body.hostname, brandName: body.brandName }, "Ad event tracked"); + set.status = 204; return; }, { query: t.Object({ - url: t.String(), + url: t.Optional(t.String()), + }), + body: t.Object({ + type: t.Optional(t.Union([ + t.Literal("impression"), + t.Literal("click"), + t.Literal("dismiss"), + ])), + sessionId: t.String(), + hostname: t.Optional(t.String()), + brandName: t.Optional(t.String()), + adTitle: t.Optional(t.String()), + adText: t.Optional(t.String()), + clickUrl: t.Optional(t.String()), + impUrl: t.Optional(t.String()), + cta: t.Optional(t.String()), + favicon: t.Optional(t.String()), + deviceType: t.Optional(t.String()), + os: t.Optional(t.String()), + browser: t.Optional(t.String()), }), } ) From b87e59fd22f8ec57e8c8d7abc9b19863d3d1a1b0 Mon Sep 17 00:00:00 2001 From: KartikLabhshetwar Date: Thu, 5 Feb 2026 12:49:52 +0530 Subject: [PATCH 2/6] refactor: improve gravity route URL validation and analytics tracking --- server/routes/gravity.ts | 61 ++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/server/routes/gravity.ts b/server/routes/gravity.ts index 171ee714..7d4ea198 100644 --- a/server/routes/gravity.ts +++ b/server/routes/gravity.ts @@ -83,37 +83,48 @@ export const gravityRoutes = new Elysia({ prefix: "/api" }) const impUrl = query.url; const eventType = body.type || "impression"; - // For impressions, forward to Gravity for billing + // Track validation/forwarding status for analytics + let gravityForwarded = false; + let validationError = ""; + + // For impressions, validate and forward to Gravity for billing if (eventType === "impression" && impUrl) { // Validate URL is from Gravity + let isValidUrl = false; try { const parsedUrl = new URL(impUrl); - if (!parsedUrl.hostname.endsWith("trygravity.ai")) { - set.status = 400; - return { error: "Invalid impression URL" }; + if (parsedUrl.hostname.endsWith("trygravity.ai")) { + isValidUrl = true; + } else { + validationError = "Invalid impression URL: not from trygravity.ai"; + logger.warn({ impUrl }, validationError); } } catch { - set.status = 400; - return { error: "Invalid URL format" }; + validationError = "Invalid URL format"; + logger.warn({ impUrl }, validationError); } - // Forward the impression request to Gravity - try { - logger.info({ impUrl }, "Forwarding impression to Gravity"); - const response = await fetch(impUrl, { - method: "GET", - headers: { - "User-Agent": "13ft-impression-proxy/1.0", - }, - }); - await response.text().catch(() => {}); - logger.info({ impUrl, status: response.status }, "Impression forwarded successfully"); - } catch (error) { - logger.warn({ impUrl, error: String(error) }, "Failed to forward impression"); + // Forward to Gravity only if URL is valid + if (isValidUrl) { + try { + logger.info({ impUrl }, "Forwarding impression to Gravity"); + const response = await fetch(impUrl, { + method: "GET", + headers: { + "User-Agent": "13ft-impression-proxy/1.0", + }, + }); + await response.text().catch(() => {}); + gravityForwarded = true; + logger.info({ impUrl, status: response.status }, "Impression forwarded successfully"); + } catch (error) { + validationError = `Failed to forward: ${String(error)}`; + logger.warn({ impUrl, error: String(error) }, "Failed to forward impression"); + } } } - // Track event locally for analytics + // Always track event locally for analytics (even if Gravity forwarding failed) trackAdEvent({ event_type: eventType as "impression" | "click" | "dismiss", session_id: body.sessionId, @@ -129,8 +140,16 @@ export const gravityRoutes = new Elysia({ prefix: "/api" }) os: body.os || "", browser: body.browser || "", status: "filled", + gravity_status_code: gravityForwarded ? 200 : 0, + error_message: validationError, }); - logger.debug({ type: eventType, hostname: body.hostname, brandName: body.brandName }, "Ad event tracked"); + logger.debug({ + type: eventType, + hostname: body.hostname, + brandName: body.brandName, + gravityForwarded, + validationError: validationError || undefined, + }, "Ad event tracked"); set.status = 204; return; From 694e0678c5557a504456c38a2288644679734e1f Mon Sep 17 00:00:00 2001 From: KartikLabhshetwar Date: Thu, 5 Feb 2026 12:55:05 +0530 Subject: [PATCH 3/6] fix: handle Gravity response status and timeouts correctly --- server/routes/gravity.ts | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/server/routes/gravity.ts b/server/routes/gravity.ts index 7d4ea198..f9d699e6 100644 --- a/server/routes/gravity.ts +++ b/server/routes/gravity.ts @@ -84,7 +84,7 @@ export const gravityRoutes = new Elysia({ prefix: "/api" }) const eventType = body.type || "impression"; // Track validation/forwarding status for analytics - let gravityForwarded = false; + let gravityStatusCode = 0; let validationError = ""; // For impressions, validate and forward to Gravity for billing @@ -106,6 +106,9 @@ export const gravityRoutes = new Elysia({ prefix: "/api" }) // Forward to Gravity only if URL is valid if (isValidUrl) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), GRAVITY_TIMEOUT_MS); + try { logger.info({ impUrl }, "Forwarding impression to Gravity"); const response = await fetch(impUrl, { @@ -113,13 +116,24 @@ export const gravityRoutes = new Elysia({ prefix: "/api" }) headers: { "User-Agent": "13ft-impression-proxy/1.0", }, + signal: controller.signal, }); + clearTimeout(timeoutId); await response.text().catch(() => {}); - gravityForwarded = true; - logger.info({ impUrl, status: response.status }, "Impression forwarded successfully"); + gravityStatusCode = response.status; + + if (response.ok) { + logger.info({ impUrl, status: response.status }, "Impression forwarded successfully"); + } else { + validationError = `Gravity returned ${response.status}`; + logger.warn({ impUrl, status: response.status }, "Gravity returned non-2xx status"); + } } catch (error) { - validationError = `Failed to forward: ${String(error)}`; - logger.warn({ impUrl, error: String(error) }, "Failed to forward impression"); + clearTimeout(timeoutId); + const errorMsg = String(error); + const isTimeout = errorMsg.includes("abort"); + validationError = isTimeout ? "Timeout forwarding to Gravity" : `Failed to forward: ${errorMsg}`; + logger.warn({ impUrl, error: errorMsg, isTimeout }, "Failed to forward impression"); } } } @@ -140,14 +154,14 @@ export const gravityRoutes = new Elysia({ prefix: "/api" }) os: body.os || "", browser: body.browser || "", status: "filled", - gravity_status_code: gravityForwarded ? 200 : 0, + gravity_status_code: gravityStatusCode, error_message: validationError, }); logger.debug({ type: eventType, hostname: body.hostname, brandName: body.brandName, - gravityForwarded, + gravityStatusCode, validationError: validationError || undefined, }, "Ad event tracked"); From fbac41ea0f3673dfdb25ca9673486580551cd7ad Mon Sep 17 00:00:00 2001 From: Kartik Labhshetwar Date: Thu, 5 Feb 2026 12:59:02 +0530 Subject: [PATCH 4/6] Update server/routes/gravity.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- server/routes/gravity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routes/gravity.ts b/server/routes/gravity.ts index f9d699e6..a6337d50 100644 --- a/server/routes/gravity.ts +++ b/server/routes/gravity.ts @@ -81,7 +81,7 @@ export const gravityRoutes = new Elysia({ prefix: "/api" }) "/px", async ({ query, body, set }) => { const impUrl = query.url; - const eventType = body.type || "impression"; + const eventType = body.type; // Track validation/forwarding status for analytics let gravityStatusCode = 0; From ff6fbf8f45b41957e8c52aeea71848bc425cd61d Mon Sep 17 00:00:00 2001 From: Kartik Labhshetwar Date: Thu, 5 Feb 2026 12:59:17 +0530 Subject: [PATCH 5/6] Update server/routes/gravity.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- server/routes/gravity.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/routes/gravity.ts b/server/routes/gravity.ts index a6337d50..6ada44e3 100644 --- a/server/routes/gravity.ts +++ b/server/routes/gravity.ts @@ -173,11 +173,11 @@ export const gravityRoutes = new Elysia({ prefix: "/api" }) url: t.Optional(t.String()), }), body: t.Object({ - type: t.Optional(t.Union([ + type: t.Union([ t.Literal("impression"), t.Literal("click"), t.Literal("dismiss"), - ])), + ]), sessionId: t.String(), hostname: t.Optional(t.String()), brandName: t.Optional(t.String()), From be20a7596ede605af76761b9d3b886cfb41aec2d Mon Sep 17 00:00:00 2001 From: KartikLabhshetwar Date: Thu, 5 Feb 2026 13:09:08 +0530 Subject: [PATCH 6/6] refactor: add forwarding_failed status and enhance Gravity impression URL validation --- lib/clickhouse.ts | 3 +- server/routes/gravity.ts | 107 +++++++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 46 deletions(-) diff --git a/lib/clickhouse.ts b/lib/clickhouse.ts index 6767fbba..a391f66f 100644 --- a/lib/clickhouse.ts +++ b/lib/clickhouse.ts @@ -115,7 +115,8 @@ export interface AnalyticsEvent { // ============================================================================= // Ad event status - matches ContextResponseStatus in types/api.ts -export type AdEventStatus = "filled" | "no_fill" | "premium_user" | "gravity_error" | "timeout" | "error"; +// "forwarding_failed" is for impression events where Gravity URL forwarding failed +export type AdEventStatus = "filled" | "no_fill" | "premium_user" | "gravity_error" | "timeout" | "error" | "forwarding_failed"; // Event type for tracking funnel: request -> impression -> click/dismiss export type AdEventType = "request" | "impression" | "click" | "dismiss"; diff --git a/server/routes/gravity.ts b/server/routes/gravity.ts index 6ada44e3..8742c0e3 100644 --- a/server/routes/gravity.ts +++ b/server/routes/gravity.ts @@ -80,64 +80,80 @@ export const gravityRoutes = new Elysia({ prefix: "/api" }) .post( "/px", async ({ query, body, set }) => { - const impUrl = query.url; + // Prefer query.url, fall back to body.impUrl for impressions + const impUrl = query.url || body.impUrl; const eventType = body.type; // Track validation/forwarding status for analytics let gravityStatusCode = 0; let validationError = ""; + let forwardingSuccess = false; // For impressions, validate and forward to Gravity for billing - if (eventType === "impression" && impUrl) { - // Validate URL is from Gravity - let isValidUrl = false; - try { - const parsedUrl = new URL(impUrl); - if (parsedUrl.hostname.endsWith("trygravity.ai")) { - isValidUrl = true; - } else { - validationError = "Invalid impression URL: not from trygravity.ai"; + if (eventType === "impression") { + if (!impUrl) { + // Flag missing impression URL - this is a client bug + validationError = "Missing impression URL"; + logger.warn({ sessionId: body.sessionId, hostname: body.hostname }, validationError); + } else { + // Validate URL is from Gravity + let isValidUrl = false; + try { + const parsedUrl = new URL(impUrl); + if (parsedUrl.hostname.endsWith("trygravity.ai")) { + isValidUrl = true; + } else { + validationError = "Invalid impression URL: not from trygravity.ai"; + logger.warn({ impUrl }, validationError); + } + } catch { + validationError = "Invalid URL format"; logger.warn({ impUrl }, validationError); } - } catch { - validationError = "Invalid URL format"; - logger.warn({ impUrl }, validationError); - } - - // Forward to Gravity only if URL is valid - if (isValidUrl) { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), GRAVITY_TIMEOUT_MS); - try { - logger.info({ impUrl }, "Forwarding impression to Gravity"); - const response = await fetch(impUrl, { - method: "GET", - headers: { - "User-Agent": "13ft-impression-proxy/1.0", - }, - signal: controller.signal, - }); - clearTimeout(timeoutId); - await response.text().catch(() => {}); - gravityStatusCode = response.status; - - if (response.ok) { - logger.info({ impUrl, status: response.status }, "Impression forwarded successfully"); - } else { - validationError = `Gravity returned ${response.status}`; - logger.warn({ impUrl, status: response.status }, "Gravity returned non-2xx status"); + // Forward to Gravity only if URL is valid + if (isValidUrl) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), GRAVITY_TIMEOUT_MS); + + try { + logger.info({ impUrl }, "Forwarding impression to Gravity"); + const response = await fetch(impUrl, { + method: "GET", + headers: { + "User-Agent": "13ft-impression-proxy/1.0", + }, + signal: controller.signal, + }); + clearTimeout(timeoutId); + await response.text().catch(() => {}); + gravityStatusCode = response.status; + + if (response.ok) { + forwardingSuccess = true; + logger.info({ impUrl, status: response.status }, "Impression forwarded successfully"); + } else { + validationError = `Gravity returned ${response.status}`; + logger.warn({ impUrl, status: response.status }, "Gravity returned non-2xx status"); + } + } catch (error) { + clearTimeout(timeoutId); + const errorMsg = String(error); + const isTimeout = errorMsg.includes("abort"); + validationError = isTimeout ? "Timeout forwarding to Gravity" : `Failed to forward: ${errorMsg}`; + logger.warn({ impUrl, error: errorMsg, isTimeout }, "Failed to forward impression"); } - } catch (error) { - clearTimeout(timeoutId); - const errorMsg = String(error); - const isTimeout = errorMsg.includes("abort"); - validationError = isTimeout ? "Timeout forwarding to Gravity" : `Failed to forward: ${errorMsg}`; - logger.warn({ impUrl, error: errorMsg, isTimeout }, "Failed to forward impression"); } } } + let trackingStatus: "filled" | "forwarding_failed"; + if (eventType === "impression") { + trackingStatus = forwardingSuccess ? "filled" : "forwarding_failed"; + } else { + trackingStatus = "filled"; + } + // Always track event locally for analytics (even if Gravity forwarding failed) trackAdEvent({ event_type: eventType as "impression" | "click" | "dismiss", @@ -147,13 +163,13 @@ export const gravityRoutes = new Elysia({ prefix: "/api" }) ad_title: body.adTitle || "", ad_text: body.adText || "", click_url: body.clickUrl || "", - imp_url: body.impUrl || impUrl || "", + imp_url: impUrl || "", cta: body.cta || "", favicon: body.favicon || "", device_type: body.deviceType || "", os: body.os || "", browser: body.browser || "", - status: "filled", + status: trackingStatus, gravity_status_code: gravityStatusCode, error_message: validationError, }); @@ -161,6 +177,7 @@ export const gravityRoutes = new Elysia({ prefix: "/api" }) type: eventType, hostname: body.hostname, brandName: body.brandName, + status: trackingStatus, gravityStatusCode, validationError: validationError || undefined, }, "Ad event tracked");