Skip to content

fix: consoldating two ad tracking into one#60

Open
KartikLabhshetwar wants to merge 6 commits intomainfrom
fix/dupicate-ad-api-routes
Open

fix: consoldating two ad tracking into one#60
KartikLabhshetwar wants to merge 6 commits intomainfrom
fix/dupicate-ad-api-routes

Conversation

@KartikLabhshetwar
Copy link
Collaborator

@KartikLabhshetwar KartikLabhshetwar commented Feb 5, 2026


Open with Devin

Summary by CodeRabbit

  • Refactor

    • Unified ad event handling: impressions, clicks, and dismissals now funnel through a single tracking flow; events are only sent when a valid session exists and impression requests include the original impression URL.
    • Server /px handler expanded: accepts richer event payloads, validates/forwards impression URLs to the ad provider with timeout handling, logs forwarding status, and records local analytics for every event (non-blocking 204 response).
  • Chores

    • Removed legacy adtrack route and simplified routing to the unified public handler.

@paragon-review
Copy link

paragon-review bot commented Feb 5, 2026

Paragon Review Unavailable

Hi @KartikLabhshetwar! To enable Paragon reviews on this repository, please register at https://home.polarity.cc

Once registered, connect your GitHub account and Paragon will automatically review your pull requests.

@vercel
Copy link

vercel bot commented Feb 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
smry Ready Ready Preview, Comment Feb 5, 2026 7:40am

Request Review

@railway-app
Copy link

railway-app bot commented Feb 5, 2026

🚅 Deployed to the SMRY-pr-60 environment in smry

Service Status Web Updated (UTC)
SMRY ✅ Success (View Logs) Web Feb 5, 2026 at 7:40 am
smry-api ✅ Success (View Logs) Feb 5, 2026 at 7:40 am

@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

📝 Walkthrough

Walkthrough

Consolidates client ad tracking to a single /api/px flow: removes /api/adtrack, refactors use-gravity-ad to call a non-null sendAdEvent, and updates /api/px to validate/forward impressions to Gravity and record local analytics for all ad events.

Changes

Cohort / File(s) Summary
Client-side tracking consolidation
lib/hooks/use-gravity-ad.ts
Replaced nullable `sendTrackingEvent(ad: ContextAd
Removed adtrack route
server/routes/adtrack.ts, server/index.ts
Deleted server/routes/adtrack.ts and removed its registration from server/index.ts. The dedicated /api/adtrack POST endpoint and wiring were removed.
Unified /api/px handling & analytics
server/routes/gravity.ts
Expanded /api/px to accept optional query url and richer request body (type, sessionId, hostname, brandName, adTitle, adText, clickUrl, impUrl, cta, favicon, deviceType, os, browser). Impressions validate impUrl (must end with trygravity.ai), proxy valid impressions to Gravity (GET with UA 13ft-impression-proxy/1.0, 6s timeout), record gravity_status_code and forwarding success, log errors/validation failures, and call trackAdEvent for local analytics. Handler signature now reads (async ({ query, body, set }) => ...).
Analytics type update
lib/clickhouse.ts
Added new AdEventStatus literal "forwarding_failed" to the union for ad event status tracking.

Sequence Diagram

sequenceDiagram
    participant Client as Client (use-gravity-ad)
    participant PxEndpoint as /api/px
    participant Local as trackAdEvent (Local Analytics)
    participant Gravity as Gravity Service

    Client->>PxEndpoint: POST /api/px (body: type, sessionId, metadata) or POST /api/px?url=encodedImpUrl
    PxEndpoint->>PxEndpoint: determine event type (body.type or query)
    alt event == "impression" and impUrl/query.url present
        PxEndpoint->>PxEndpoint: validate impUrl endsWith "trygravity.ai"
        alt valid
            PxEndpoint->>Gravity: GET impUrl (UA: "13ft-impression-proxy/1.0", timeout 6s)
            Gravity-->>PxEndpoint: response (status)
            PxEndpoint->>PxEndpoint: set gravity_status_code / forwardingSuccess
        else invalid
            PxEndpoint-->>PxEndpoint: set validationError / log
        end
    end
    PxEndpoint->>Local: trackAdEvent(payload with event_type, sessionId, metadata, gravity_status_code?, error?)
    Local-->>PxEndpoint: ack
    PxEndpoint-->>Client: 204 No Content
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • mrmps

Poem

🐰
I hopped through hooks and routes today,
One pixel path now leads the way.
Impressions ping, clicks find track,
All tiny hops bring data back.
A merry twitch for tidy stats. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: consolidating two separate ad tracking flows (the old /api/adtrack endpoint and the fireImpression proxy behavior) into a single unified tracking system via /api/px.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/dupicate-ad-api-routes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@server/routes/gravity.ts`:
- Around line 87-98: The domain check in the impression handling (eventType,
impUrl, parsedUrl.hostname) is too permissive because
hostname.endsWith("trygravity.ai") will allow names like "evil-trygravity.ai";
change the validation to lower-case the hostname and allow only exact
"trygravity.ai" or subdomains by requiring a leading dot for subdomains (i.e.,
hostname === "trygravity.ai" || hostname.endsWith(".trygravity.ai")) so spoofed
hostnames without the dot are rejected; update the try/catch block where
parsedUrl is created to use this stricter check and return the same 400 error on
failure.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 5, 2026

Greptile Overview

Greptile Summary

This PR consolidates two separate ad tracking endpoints (/api/adtrack and /api/px) into a single unified /api/px endpoint that handles all ad events (impressions, clicks, dismissals).

Key Changes:

  • Removed legacy /api/adtrack route and deleted server/routes/adtrack.ts
  • Unified all ad event tracking through /api/px endpoint in server/routes/gravity.ts
  • For impressions: server now validates and forwards the Gravity impression URL for billing, tracks forwarding success/failure, and records local analytics
  • For clicks/dismisses: server only tracks local analytics (no Gravity forwarding needed)
  • Added forwarding_failed status to AdEventStatus type to track impression forwarding failures
  • Client-side hook simplified - removed redundant impression forwarding logic

Architecture:
The refactoring improves the tracking flow by centralizing all ad event handling server-side, enabling better monitoring of Gravity API interactions and more consistent analytics. All events now follow the same code path with appropriate handling based on event type.

Confidence Score: 4/5

  • Safe to merge with minor refinements recommended
  • The refactoring is well-structured and consolidates ad tracking logic effectively. One logical issue identified with unsafe type casting that should be addressed. The redundant impUrl passing (query + body) is a minor style concern but doesn't affect functionality.
  • server/routes/gravity.ts requires attention for type casting safety on line 159

Important Files Changed

Filename Overview
lib/hooks/use-gravity-ad.ts Unified ad tracking through /api/px endpoint, removed redundant impression forwarding
server/routes/gravity.ts Unified /api/px endpoint now handles all ad events with impression forwarding, validation, and local analytics tracking

Sequence Diagram

sequenceDiagram
    participant Client as Client (use-gravity-ad.ts)
    participant API as Server (/api/px)
    participant Gravity as Gravity AI
    participant Analytics as ClickHouse

    Note over Client,Analytics: New Unified Ad Tracking Flow

    rect rgb(240, 248, 255)
        Note over Client,API: Impression Event
        Client->>API: POST /api/px?url={impUrl}<br/>body: {type: "impression", ...ad data}
        API->>API: Validate impUrl from trygravity.ai
        alt Valid impression URL
            API->>Gravity: GET {impUrl}<br/>(forward for billing)
            Gravity-->>API: 200 OK
            API->>Analytics: trackAdEvent(status: "filled")
        else Invalid/Missing URL
            API->>Analytics: trackAdEvent(status: "forwarding_failed")
        else Timeout/Error
            API->>Analytics: trackAdEvent(status: "forwarding_failed")
        end
        API-->>Client: 204 No Content
    end

    rect rgb(255, 248, 240)
        Note over Client,Analytics: Click Event
        Client->>API: POST /api/px<br/>body: {type: "click", ...ad data}
        API->>Analytics: trackAdEvent(status: "filled")
        API-->>Client: 204 No Content
    end

    rect rgb(248, 255, 240)
        Note over Client,Analytics: Dismiss Event
        Client->>API: POST /api/px<br/>body: {type: "dismiss", ...ad data}
        API->>Analytics: trackAdEvent(status: "filled")
        API-->>Client: 204 No Content
    end

    Note over Client,Analytics: Key Changes:<br/>1. Single /api/px endpoint (removed /adtrack)<br/>2. Server-side Gravity forwarding for impressions<br/>3. All events tracked locally<br/>4. Non-blocking 204 responses
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

{
query: t.Object({
url: t.String(),
url: t.Optional(t.String()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

url field is marked optional but should be required for consistency

Suggested change
url: t.Optional(t.String()),
url: t.String(),

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 7 additional findings in Devin Review.

Open in Devin Review

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@server/routes/gravity.ts`:
- Around line 107-124: The impression-forwarding fetch inside the isValidUrl
branch should use an AbortController with the existing GRAVITY_TIMEOUT_MS to
avoid hanging: create an AbortController, pass controller.signal to
fetch(impUrl, { method: "GET", headers: {...}, signal }), start a timeout that
calls controller.abort() after GRAVITY_TIMEOUT_MS, and clear that timeout after
the fetch completes; update the try/catch around the fetch in the block that
sets gravityForwarded and logger to handle aborted requests (identify via
error.name === 'AbortError') and include that context in validationError/logger
messages for impUrl and response.status where available.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@server/routes/gravity.ts`:
- Around line 141-158: Change the hard-coded status:"filled" in the trackAdEvent
call to reflect actual forwarding outcome: derive status as "filled" only when
there is no validationError and gravityStatusCode indicates success (2xx),
otherwise set a failure status like "forward_failed" (or "validation_error" when
validationError exists); include validationError and gravityStatusCode already
present so analytics can distinguish validation failures, HTTP non‑2xx
responses, and timeouts—update the status expression in the trackAdEvent payload
(the call to trackAdEvent, referencing eventType, validationError,
gravityStatusCode and body.sessionId) accordingly.
- Around line 82-108: The impression handler currently only reads query.url into
impUrl and may silently accept missing URLs; change the code that sets impUrl to
first try query.url then fall back to body.impUrl (e.g., impUrl = query.url ||
body.impUrl), and if eventType === "impression" and impUrl is still falsy, set
validationError to a clear message like "Missing impression URL", log a warning
(using logger.warn) and ensure isValidUrl remains false so the forwarding branch
(the block guarded by isValidUrl) is skipped; keep the existing URL
parsing/hostname check (parsedUrl.hostname.endsWith("trygravity.ai")) and
preserve gravityStatusCode behavior.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

// Return 204 No Content - the client doesn't need a response
// Always track event locally for analytics (even if Gravity forwarding failed)
trackAdEvent({
event_type: eventType as "impression" | "click" | "dismiss",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eventType is cast without validation - if body.type is undefined, line 85 defaults to undefined, causing this cast to be unsafe

Suggested change
event_type: eventType as "impression" | "click" | "dismiss",
event_type: eventType || "impression",

Comment on lines +309 to +310
const trackUrl = type === "impression"
? getApiUrl(`/api/px?url=${encodeURIComponent(ad.impUrl)}`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For impressions, impUrl is now sent both in query string and request body - this creates redundancy that could lead to sync issues if they differ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant