Skip to content

Conversation

goldflag
Copy link
Collaborator

@goldflag goldflag commented Sep 27, 2025

  • Added IPAPI_KEY environment variable to docker-compose configuration for cloud services.
  • Updated settings.local.json to include a new WebFetch domain for ipapi.is.
  • Enhanced geolocation functionality in the server by integrating IPAPI for IP lookups, including additional fields for company and ASN data.
  • Refactored getLocation function to utilize IPAPI when in cloud environment, improving accuracy and detail of geolocation responses.
  • Updated tracking utilities to accommodate new geolocation data structure, ensuring comprehensive analytics capabilities.

Summary by CodeRabbit

  • New Features

    • Cloud geolocation enrichment (timezone, company/ASN, VPN/proxy/Tor/crawler/datacenter), Network dashboard section, new VPN/Crawler icons, and Timezones tab; many new filter options added.
  • Refactor

    • Geolocation now does batch IP lookups and returns per‑IP enriched results; analytics and event processing are enriched with additional geo/network fields.
  • Chores

    • Added external IP lookup domain to allowlist and wired IPAPI_KEY from environment.
  • Style

    • Standardized 401 message to “Unauthorized”; adjusted event list/card layouts; removed two session badges and reduced a session timeline skeleton.

- Added IPAPI_KEY environment variable to docker-compose configuration for cloud services.
- Updated settings.local.json to include a new WebFetch domain for ipapi.is.
- Enhanced geolocation functionality in the server by integrating IPAPI for IP lookups, including additional fields for company and ASN data.
- Refactored getLocation function to utilize IPAPI when in cloud environment, improving accuracy and detail of geolocation responses.
- Updated tracking utilities to accommodate new geolocation data structure, ensuring comprehensive analytics capabilities.
Copy link

vercel bot commented Sep 27, 2025

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

Project Deployment Preview Comments Updated (UTC)
rybbit Ready Ready Preview Comment Sep 27, 2025 5:57am

Copy link
Contributor

coderabbitai bot commented Sep 27, 2025

Warning

Rate limit exceeded

@goldflag has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 10 minutes and 5 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 6be7887 and 3a019ba.

📒 Files selected for processing (3)
  • server/src/db/geolocation/geolocation.ts (2 hunks)
  • server/src/services/replay/trackingUtils.ts (1 hunks)
  • server/src/services/tracker/pageviewQueue.ts (2 hunks)

Walkthrough

Adds ipapi.is and IPAPI_KEY support for cloud geolocation; extends ClickHouse events schema with timezone/ASN/company/proxy fields; refactors geolocation to batch IP lookups returning ip→LocationResponse; updates tracking, pageview enrichment, validation, filters, and UI to consume enriched geo data; tweaks 401 message.

Changes

Cohort / File(s) Summary of changes
WebFetch permissions
\.claude/settings.local.json
Adds ipapi.is to the WebFetch allowlist.
Cloud compose env
docker-compose.cloud.yml
Adds IPAPI_KEY to backend Axiom environment variables.
ClickHouse schema migrations
server/src/db/clickhouse/clickhouse.ts
Adds multiple ALTER TABLE IF NOT EXISTS columns to events: timezone, company fields, ASN fields, vpn, crawler, datacenter, and flags (is_proxy, is_tor, is_satellite).
Geolocation service refactor
server/src/db/geolocation/geolocation.ts
Introduces IPAPIResponse and expanded LocationResponse; changes getLocation to accept ips: string[] and return Record<string, LocationResponse>; adds getLocationFromIPAPI and getLocationFromLocal; deduplicates IPs; maps company/ASN/vpn/crawler/datacenter/proxy/tor/satellite fields; guards cloud path with IS_CLOUD.
Tracker & replay changes
server/src/services/replay/trackingUtils.ts
server/src/services/tracker/pageviewQueue.ts
Calls getLocation with batched IPs ([ip] or ips[]); adapts geo accesses to geoData[ip]; removes per-IP try/catch in pageview flow; appends timezone, company/ASN/vpn/crawler/datacenter/proxy fields to pageview rows.
API validation & shared types
server/src/api/analytics/query-validation.ts
shared/src/filters.ts
Replaces older time-related schemas with fillDateParamsSchema; expands allowed filter parameters and FilterParameter union to include timezone, vpn, crawler, datacenter, company, company_type, company_domain, asn_org, asn_type, asn_domain.
Client filters & UI
client/src/app/[site]/components/shared/Filters/const.tsx
client/src/app/[site]/components/shared/Filters/utils.ts
client/src/app/[site]/main/components/sections/Countries.tsx
client/src/app/[site]/main/components/sections/Network.tsx
client/src/app/[site]/main/page.tsx
client/src/app/[site]/main/components/sections/Events.tsx
Adds new filters and label mappings (timezone, vpn, crawler, datacenter, company*, company_type, company_domain, asn_org, asn_type, asn_domain); swaps one filter icon; adds Network component and mounts it under IS_CLOUD; adds "timezones" tab; adjusts Events/Card layout heights.
Icon components
client/src/app/[site]/components/shared/icons/Crawler.tsx
client/src/app/[site]/components/shared/icons/VPN.tsx
Adds Crawler and VPN components that render vendor logos (Next.js Image) or fallbacks (Bug/Shield).
Sessions UI tweaks
client/src/components/Sessions/SessionDetails.tsx
client/src/components/Sessions/SessionCard.tsx
Removes a timeline skeleton block and two badges (Errors, Outbound Clicks) from SessionCard.
Auth message tweak
server/src/index.ts
Changes 401 Unauthorized payload from { error: "Unauthorized 1" } to { error: "Unauthorized" }.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Tracker as PageviewQueue
  participant Geo as Geolocation Service
  participant IPAPI as ipapi.is
  participant GeoDB as Local GeoIP DB
  participant CH as ClickHouse

  Client->>Tracker: Send batch of pageviews (with IPs)
  Tracker->>Geo: getLocation(ips[])
  alt Cloud mode (IS_CLOUD && IPAPI_KEY)
    Geo->>IPAPI: Batch fetch metadata for unique IPs
    IPAPI-->>Geo: Map of IP → enriched LocationResponse
  else Local mode
    loop per unique IP
      Geo->>GeoDB: city(ip)
      GeoDB-->>Geo: nullable geo result
    end
  end
  Geo-->>Tracker: Record<string, LocationResponse>
  Tracker->>CH: Bulk insert pageviews enriched with new fields
  CH-->>Tracker: Ack
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

Hop hop, I chased each IP with glee,
Timezones, ASNs, and companies I see.
One batch call now fetches every clue,
Enriched events sprout fields fresh and new.
Carrot cheers — hops and telemetry too! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly captures the primary change of integrating IPAPI to enrich geolocation data and aligns with the objectives and overall modifications described in the pull request.

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
Contributor

@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

🧹 Nitpick comments (1)
server/src/db/geolocation/geolocation.ts (1)

206-222: Guard against NaN abuse scores and spurious company/ASN objects.

When IPAPI omits the company/asn blocks or their abuse scores, the current code still instantiates those sub-objects and calls Number(undefined), yielding NaN. That leaks into downstream analytics payloads and storage even though no enrichment data exists. Let’s only build those objects when the API actually returned them, and only parse the abuse score when we have a string to work with.

-      results[ip] = {
+      const companyAbuseScore = item.company?.abuser_score;
+      const asnAbuseScore = item.asn?.abuser_score;
+
+      results[ip] = {
         city: item.location?.city,
         country: item.location?.country,
         countryIso: item.location?.country_code,
         latitude: item.location?.latitude,
         longitude: item.location?.longitude,
         timeZone: item.location?.timezone,
         vpn: item.vpn?.service,
         crawler: typeof item.is_crawler === "string" ? item.is_crawler : undefined,
         datacenter: item.datacenter?.datacenter,
         isProxy: item.is_proxy,
         isTor: item.is_tor,
         isSatellite: item.is_satellite,
-
-        company: {
-          name: item.company?.name,
-          domain: item.company?.domain,
-          type: item.company?.type,
-          abuseScore: Number(item.company?.abuser_score?.split(" ")[0]),
-        },
-
-        asn: {
-          asn: item.asn?.asn,
-          org: item.asn?.org,
-          domain: item.asn?.domain,
-          type: item.asn?.type,
-          abuseScore: Number(item.asn?.abuser_score?.split(" ")[0]),
-        },
+        company: item.company
+          ? {
+              name: item.company.name,
+              domain: item.company.domain,
+              type: item.company.type,
+              abuseScore: companyAbuseScore ? Number.parseFloat(companyAbuseScore.split(" ")[0]) : undefined,
+            }
+          : undefined,
+        asn: item.asn
+          ? {
+              asn: item.asn.asn,
+              org: item.asn.org,
+              domain: item.asn.domain,
+              type: item.asn.type,
+              abuseScore: asnAbuseScore ? Number.parseFloat(asnAbuseScore.split(" ")[0]) : undefined,
+            }
+          : undefined,
       };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 331e5c8 and 8d338c1.

📒 Files selected for processing (7)
  • .claude/settings.local.json (1 hunks)
  • docker-compose.cloud.yml (1 hunks)
  • server/src/db/clickhouse/clickhouse.ts (1 hunks)
  • server/src/db/geolocation/geolocation.ts (4 hunks)
  • server/src/index.ts (1 hunks)
  • server/src/services/replay/trackingUtils.ts (1 hunks)
  • server/src/services/tracker/pageviewQueue.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{client,server}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

{client,server}/**/*.{ts,tsx}: Use TypeScript with strict typing throughout both client and server
Use try/catch blocks with specific error types for error handling
Use camelCase for variables and functions, PascalCase for components and types
Group imports by external, then internal, and sort alphabetically within groups

Files:

  • server/src/db/geolocation/geolocation.ts
  • server/src/services/tracker/pageviewQueue.ts
  • server/src/services/replay/trackingUtils.ts
  • server/src/index.ts
  • server/src/db/clickhouse/clickhouse.ts
server/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Backend: Use Fastify, Drizzle ORM (Postgres), ClickHouse, and Zod

Files:

  • server/src/db/geolocation/geolocation.ts
  • server/src/services/tracker/pageviewQueue.ts
  • server/src/services/replay/trackingUtils.ts
  • server/src/index.ts
  • server/src/db/clickhouse/clickhouse.ts
🧬 Code graph analysis (3)
server/src/db/geolocation/geolocation.ts (1)
server/src/lib/logger/logger.ts (1)
  • logger (67-67)
server/src/services/tracker/pageviewQueue.ts (1)
server/src/db/geolocation/geolocation.ts (1)
  • getLocation (229-253)
server/src/services/replay/trackingUtils.ts (1)
server/src/db/geolocation/geolocation.ts (1)
  • getLocation (229-253)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Client Images (ubuntu-24.04-arm, linux/arm64)
  • GitHub Check: Build Client Images (ubuntu-latest, linux/amd64)

Comment on lines 40 to 46
const geoData = await getLocation([ipAddress]);

const countryCode = geoData?.countryIso || "";
const regionCode = geoData?.subdivisions?.[0]?.isoCode || "";
const latitude = geoData?.latitude || 0;
const longitude = geoData?.longitude || 0;
const city = geoData?.city || "";
const countryCode = geoData?.[ipAddress]?.countryIso || "";
const regionCode = geoData?.[ipAddress]?.subdivisions?.[0]?.isoCode || "";
const latitude = geoData?.[ipAddress]?.latitude || 0;
const longitude = geoData?.[ipAddress]?.longitude || 0;
const city = geoData?.[ipAddress]?.city || "";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reintroduce defensive geo lookup handling

Dropping the try/catch means any transient network/IPAPI failure now rejects parseTrackingData, so replay/pageview ingestion blows up instead of gracefully omitting geo info as before. Please restore the guard so we still emit tracking data when lookup fails.

-  const geoData = await getLocation([ipAddress]);
+  const geoData = await getLocation([ipAddress]).catch(_error => {
+    return {} as Awaited<ReturnType<typeof getLocation>>;
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const geoData = await getLocation([ipAddress]);
const countryCode = geoData?.countryIso || "";
const regionCode = geoData?.subdivisions?.[0]?.isoCode || "";
const latitude = geoData?.latitude || 0;
const longitude = geoData?.longitude || 0;
const city = geoData?.city || "";
const countryCode = geoData?.[ipAddress]?.countryIso || "";
const regionCode = geoData?.[ipAddress]?.subdivisions?.[0]?.isoCode || "";
const latitude = geoData?.[ipAddress]?.latitude || 0;
const longitude = geoData?.[ipAddress]?.longitude || 0;
const city = geoData?.[ipAddress]?.city || "";
const geoData = await getLocation([ipAddress]).catch(_error => {
return {} as Awaited<ReturnType<typeof getLocation>>;
});
const countryCode = geoData?.[ipAddress]?.countryIso || "";
const regionCode = geoData?.[ipAddress]?.subdivisions?.[0]?.isoCode || "";
const latitude = geoData?.[ipAddress]?.latitude || 0;
const longitude = geoData?.[ipAddress]?.longitude || 0;
const city = geoData?.[ipAddress]?.city || "";
🤖 Prompt for AI Agents
In server/src/services/replay/trackingUtils.ts around lines 40 to 46, the direct
call to getLocation([ipAddress]) can throw and now bubbles up; reintroduce
defensive handling by wrapping the geo lookup in a try/catch, on success extract
countryCode/regionCode/latitude/longitude/city as currently done, and on any
error (or if geoData is undefined) set those variables to their safe defaults
(empty strings or zeros) and optionally log a debug/warn message; ensure
parseTrackingData continues and returns tracking data without geo info when
lookup fails.

Comment on lines +45 to 46
const geoData = await getLocation(ips);

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Prevent getLocation failures from freezing the queue

await getLocation(ips) now executes before the try/finally that resets this.processing. If IPAPI errors (network, quota, etc.), the rejection propagates, processing stays true, and the queue stops draining. Please catch the lookup failure, log it, and continue with empty geo data so the worker keeps running.

-    const geoData = await getLocation(ips);
+    let geoData: Awaited<ReturnType<typeof getLocation>> | undefined;
+    try {
+      geoData = await getLocation(ips);
+    } catch (error) {
+      this.logger.error(error, "Failed to fetch geo data");
+      geoData = {} as Awaited<ReturnType<typeof getLocation>>;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const geoData = await getLocation(ips);
// Replace the single await call with a safe wrapper
let geoData: Awaited<ReturnType<typeof getLocation>> | undefined;
try {
geoData = await getLocation(ips);
} catch (error) {
this.logger.error(error, "Failed to fetch geo data");
geoData = {} as Awaited<ReturnType<typeof getLocation>>;
}
🤖 Prompt for AI Agents
In server/src/services/tracker/pageviewQueue.ts around lines 45-46, calling
await getLocation(ips) occurs before the try/finally that resets this.processing
which lets failures leave processing=true and freeze the queue; wrap the
getLocation call in a try/catch (or move it inside the existing try) so any
rejection is caught, log the error with context, and fall back to an
empty/default geoData object so processing continues; keep the finally block
that sets this.processing = false untouched so the flag is always cleared.

…flags

- Updated the handling of is_proxy, is_tor, and is_satellite properties to set them to null if they are undefined, improving data consistency in the pageviewQueue service.
- Updated the assignment of company_abuse_score and asn_abuse_score to use nullish coalescing for better handling of undefined values.
- Simplified the assignment of is_proxy, is_tor, and is_satellite properties to default to null if they are undefined, enhancing data consistency in the pageviewQueue service.
- Introduced new filter options for timezone, VPN, crawler, datacenter, company, company type, company domain, ASN org, ASN type, and ASN domain in the Filters component.
- Updated the getParameterNameLabel function to include labels for the new filter parameters.
- Enhanced the Countries component to support a new tab for timezones.
- Modified the Events component layout for improved display.
- Updated query validation and shared filter types to accommodate new parameters.
Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
client/src/app/[site]/main/components/sections/Network.tsx (1)

52-86: Prefer user-friendly boolean labels in Network tabs

Right now the VPN/Crawler/Datacenter rows surface the literal true/false strings, which reads a bit raw in the UI. A tiny helper keeps the filter payloads untouched while presenting “Yes”/“No” (with an “Unknown” fallback) to match how we phrase other boolean pivots.

 export function Network() {
   const [tab, setTab] = useState<Tab>("vpn");
   const [expanded, setExpanded] = useState(false);
   const close = () => {
     setExpanded(false);
   };
 
+  const formatBooleanLabel = (value: string | null | undefined) => {
+    if (value === "true") return "Yes";
+    if (value === "false") return "No";
+    return "Unknown";
+  };
+
   return (
@@
           <TabsContent value="vpn">
             <StandardSection
               filterParameter="vpn"
               title="VPN"
               getValue={e => e.value}
               getKey={e => e.value}
-              getFilterLabel={e => e.value}
+              getFilterLabel={e => formatBooleanLabel(e.value)}
               expanded={expanded}
               close={close}
-              getLabel={e => e.value}
+              getLabel={e => formatBooleanLabel(e.value)}
             />
           </TabsContent>
           <TabsContent value="crawler">
             <StandardSection
               filterParameter="crawler"
               title="Crawler"
               getValue={e => e.value}
               getKey={e => e.value}
-              getFilterLabel={e => e.value}
+              getFilterLabel={e => formatBooleanLabel(e.value)}
               expanded={expanded}
               close={close}
-              getLabel={e => e.value}
+              getLabel={e => formatBooleanLabel(e.value)}
             />
           </TabsContent>
           <TabsContent value="datacenter">
             <StandardSection
               filterParameter="datacenter"
               title="Datacenter"
               getValue={e => e.value}
               getKey={e => e.value}
-              getFilterLabel={e => e.value}
+              getFilterLabel={e => formatBooleanLabel(e.value)}
               expanded={expanded}
               close={close}
-              getLabel={e => e.value}
+              getLabel={e => formatBooleanLabel(e.value)}
             />
           </TabsContent>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2dc4aaa and 3043c6b.

📒 Files selected for processing (8)
  • client/src/app/[site]/components/shared/Filters/const.tsx (4 hunks)
  • client/src/app/[site]/components/shared/Filters/utils.ts (1 hunks)
  • client/src/app/[site]/main/components/sections/Countries.tsx (3 hunks)
  • client/src/app/[site]/main/components/sections/Events.tsx (2 hunks)
  • client/src/app/[site]/main/components/sections/Network.tsx (1 hunks)
  • client/src/app/[site]/main/page.tsx (2 hunks)
  • server/src/api/analytics/query-validation.ts (1 hunks)
  • shared/src/filters.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
{client,server}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

{client,server}/**/*.{ts,tsx}: Use TypeScript with strict typing throughout both client and server
Use try/catch blocks with specific error types for error handling
Use camelCase for variables and functions, PascalCase for components and types
Group imports by external, then internal, and sort alphabetically within groups

Files:

  • client/src/app/[site]/main/components/sections/Network.tsx
  • client/src/app/[site]/components/shared/Filters/const.tsx
  • client/src/app/[site]/main/components/sections/Events.tsx
  • server/src/api/analytics/query-validation.ts
  • client/src/app/[site]/main/page.tsx
  • client/src/app/[site]/main/components/sections/Countries.tsx
  • client/src/app/[site]/components/shared/Filters/utils.ts
client/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Frontend: Use Next.js, Tailwind CSS, Shadcn UI, Tanstack Query, Zustand, Luxon, Nivo, and react-hook-form

Files:

  • client/src/app/[site]/main/components/sections/Network.tsx
  • client/src/app/[site]/components/shared/Filters/const.tsx
  • client/src/app/[site]/main/components/sections/Events.tsx
  • client/src/app/[site]/main/page.tsx
  • client/src/app/[site]/main/components/sections/Countries.tsx
  • client/src/app/[site]/components/shared/Filters/utils.ts
server/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Backend: Use Fastify, Drizzle ORM (Postgres), ClickHouse, and Zod

Files:

  • server/src/api/analytics/query-validation.ts
🧬 Code graph analysis (3)
client/src/app/[site]/main/components/sections/Network.tsx (1)
client/src/app/[site]/components/shared/StandardSection/StandardSection.tsx (1)
  • StandardSection (16-132)
client/src/app/[site]/main/page.tsx (1)
client/src/app/[site]/main/components/sections/Network.tsx (1)
  • Network (21-164)
client/src/app/[site]/main/components/sections/Countries.tsx (1)
client/src/app/[site]/components/shared/StandardSection/StandardSection.tsx (1)
  • StandardSection (16-132)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Client Images (ubuntu-24.04-arm, linux/arm64)
  • GitHub Check: Build Client Images (ubuntu-latest, linux/amd64)
  • GitHub Check: Build Backend Images (ubuntu-24.04-arm, linux/arm64)
🔇 Additional comments (5)
client/src/app/[site]/components/shared/Filters/utils.ts (1)

54-73: New geo/network filter labels wired correctly

These cases cover every new FilterParameter literal so the UI keeps presenting human-friendly labels once the enriched geo data flows through. Nice sync with the shared types.

client/src/app/[site]/main/components/sections/Events.tsx (2)

24-30: Scroll limiting keeps long event lists contained

The max-h plus overflow-y-auto pairing prevents tall result sets from stretching the layout while preserving the loader overlay setup. Looks good.


64-64: Card height adjustment aligns with the new list window

Locking the card height to 405px pairs cleanly with the 344px scroll area, keeping the section visually balanced with neighboring cards.

shared/src/filters.ts (1)

28-38: Shared filter parameter union matches the new facets

The union now includes the timezone/VPN/company/ASN keys, keeping shared typing in sync with both server validation and the client filter options.

server/src/api/analytics/query-validation.ts (1)

213-222: Server filter schema updated for enriched geo fields

Adding the new literals to filterParamSchema ensures backend validation accepts the expanded geo/network filters emitted by the client and shared types.

…ng options

- Updated tab labels for VPN, Crawler, and Datacenter to be more descriptive.
- Added custom rendering for VPN and Crawler labels to include icons.
- Enhanced the display of domain labels with favicons and links for better user experience.
- Removed unnecessary skeleton loading elements from SessionDetails component for cleaner UI.
Copy link
Contributor

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3043c6b and e9b7be2.

⛔ Files ignored due to path filters (13)
  • client/public/crawlers/Ahrefs.svg is excluded by !**/*.svg
  • client/public/crawlers/Apple.svg is excluded by !**/*.svg
  • client/public/crawlers/Bing.svg is excluded by !**/*.svg
  • client/public/crawlers/DuckDuckGo.svg is excluded by !**/*.svg
  • client/public/crawlers/Google.svg is excluded by !**/*.svg
  • client/public/crawlers/Yandex.svg is excluded by !**/*.svg
  • client/public/vpns/AirVPN.svg is excluded by !**/*.svg
  • client/public/vpns/ExpressVPN.svg is excluded by !**/*.svg
  • client/public/vpns/Mullvad.svg is excluded by !**/*.svg
  • client/public/vpns/NordVPN.svg is excluded by !**/*.svg
  • client/public/vpns/PIA.svg is excluded by !**/*.svg
  • client/public/vpns/ProtonVPN.svg is excluded by !**/*.svg
  • client/public/vpns/Surfshark.svg is excluded by !**/*.svg
📒 Files selected for processing (4)
  • client/src/app/[site]/components/shared/icons/Crawler.tsx (1 hunks)
  • client/src/app/[site]/components/shared/icons/VPN.tsx (1 hunks)
  • client/src/app/[site]/main/components/sections/Network.tsx (1 hunks)
  • client/src/components/Sessions/SessionDetails.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • client/src/components/Sessions/SessionDetails.tsx
🧰 Additional context used
📓 Path-based instructions (2)
{client,server}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

{client,server}/**/*.{ts,tsx}: Use TypeScript with strict typing throughout both client and server
Use try/catch blocks with specific error types for error handling
Use camelCase for variables and functions, PascalCase for components and types
Group imports by external, then internal, and sort alphabetically within groups

Files:

  • client/src/app/[site]/components/shared/icons/VPN.tsx
  • client/src/app/[site]/components/shared/icons/Crawler.tsx
  • client/src/app/[site]/main/components/sections/Network.tsx
client/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Frontend: Use Next.js, Tailwind CSS, Shadcn UI, Tanstack Query, Zustand, Luxon, Nivo, and react-hook-form

Files:

  • client/src/app/[site]/components/shared/icons/VPN.tsx
  • client/src/app/[site]/components/shared/icons/Crawler.tsx
  • client/src/app/[site]/main/components/sections/Network.tsx
🧬 Code graph analysis (1)
client/src/app/[site]/main/components/sections/Network.tsx (4)
client/src/app/[site]/components/shared/StandardSection/StandardSection.tsx (1)
  • StandardSection (16-132)
client/src/app/[site]/components/shared/icons/VPN.tsx (1)
  • VPN (14-24)
client/src/app/[site]/components/shared/icons/Crawler.tsx (1)
  • Crawler (13-29)
client/src/components/Favicon.tsx (1)
  • Favicon (4-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Client Images (ubuntu-latest, linux/amd64)
  • GitHub Check: Build Client Images (ubuntu-24.04-arm, linux/arm64)

Comment on lines +49 to +53
<div className="w-10">
<Button size="smIcon" onClick={() => setExpanded(!expanded)}>
<Expand />
</Button>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Give the expand button an accessible label.
This icon-only trigger has no accessible name because the Expand icon is rendered aria-hidden, so assistive tech users just hear “button”. Add an aria-label (or visible text) that reflects the current action.

-              <Button size="smIcon" onClick={() => setExpanded(!expanded)}>
-                <Expand />
-              </Button>
+              <Button
+                size="smIcon"
+                onClick={() => setExpanded(!expanded)}
+                aria-label={expanded ? "Collapse network section" : "Expand network section"}
+              >
+                <Expand />
+              </Button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="w-10">
<Button size="smIcon" onClick={() => setExpanded(!expanded)}>
<Expand />
</Button>
</div>
<div className="w-10">
<Button
size="smIcon"
onClick={() => setExpanded(!expanded)}
aria-label={expanded ? "Collapse network section" : "Expand network section"}
>
<Expand />
</Button>
</div>
🤖 Prompt for AI Agents
In client/src/app/[site]/main/components/sections/Network.tsx around lines 49 to
53, the icon-only Expand button has no accessible name because the Expand icon
is aria-hidden; add an accessible label by passing an aria-label that reflects
the current state (e.g., aria-label={expanded ? "Collapse network" : "Expand
network"}) and also include aria-expanded={expanded} on the button so assistive
tech users know the action and state.

…sed on cloud environment

- Updated MainPageContent to include conditional rendering of the Network component when IS_CLOUD is true.
- Enhanced the Network component by reorganizing imports for better structure and readability.
- Removed unused tooltip elements from SessionCard to streamline the UI.
- Updated getLocation function to accept an optional parameter for local IP usage.
- Modified conditional logic to allow local geolocation retrieval when specified.
- Adjusted tracking utilities to utilize the new local IP lookup feature for improved data accuracy.
- Updated getLocation function to streamline local IP retrieval by introducing getLocationFromLocal.
- Adjusted response handling to ensure accurate mapping of IP addresses to location data.
- Enhanced conditional logic for cloud environment checks while maintaining support for local lookups.
Copy link
Contributor

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7477b9a and 6be7887.

📒 Files selected for processing (1)
  • server/src/db/geolocation/geolocation.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{client,server}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

{client,server}/**/*.{ts,tsx}: Use TypeScript with strict typing throughout both client and server
Use try/catch blocks with specific error types for error handling
Use camelCase for variables and functions, PascalCase for components and types
Group imports by external, then internal, and sort alphabetically within groups

Files:

  • server/src/db/geolocation/geolocation.ts
server/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Backend: Use Fastify, Drizzle ORM (Postgres), ClickHouse, and Zod

Files:

  • server/src/db/geolocation/geolocation.ts
🧬 Code graph analysis (1)
server/src/db/geolocation/geolocation.ts (1)
server/src/lib/logger/logger.ts (1)
  • logger (67-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Client Images (ubuntu-latest, linux/amd64)
  • GitHub Check: Build Client Images (ubuntu-24.04-arm, linux/arm64)

Comment on lines +206 to +220
company: {
name: item.company?.name,
domain: item.company?.domain,
type: item.company?.type,
abuseScore: Number(item.company?.abuser_score?.split(" ")[0]),
},

asn: {
asn: item.asn?.asn,
org: item.asn?.org,
domain: item.asn?.domain,
type: item.asn?.type,
abuseScore: Number(item.asn?.abuser_score?.split(" ")[0]),
},
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix crash when abuse_score is absent

item.company?.abuser_score?.split(" ")[0] (and the ASN variant) still indexes [0] on the result even when abuser_score is undefined, so the first IPAPI response missing that field will throw a TypeError and take down the lookup. Please guard the index before converting to a number.

-      results[ip] = {
+      const companyAbuseScoreRaw = item.company?.abuser_score;
+      const companyAbuseScore =
+        companyAbuseScoreRaw != null ? Number.parseFloat(companyAbuseScoreRaw) : undefined;
+      const asnAbuseScoreRaw = item.asn?.abuser_score;
+      const asnAbuseScore =
+        asnAbuseScoreRaw != null ? Number.parseFloat(asnAbuseScoreRaw) : undefined;
+
+      results[ip] = {
         city: item.location?.city,
@@
         company: {
           name: item.company?.name,
           domain: item.company?.domain,
           type: item.company?.type,
-          abuseScore: Number(item.company?.abuser_score?.split(" ")[0]),
+          abuseScore: companyAbuseScore,
         },
@@
         asn: {
           asn: item.asn?.asn,
           org: item.asn?.org,
           domain: item.asn?.domain,
           type: item.asn?.type,
-          abuseScore: Number(item.asn?.abuser_score?.split(" ")[0]),
+          abuseScore: asnAbuseScore,
         },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
company: {
name: item.company?.name,
domain: item.company?.domain,
type: item.company?.type,
abuseScore: Number(item.company?.abuser_score?.split(" ")[0]),
},
asn: {
asn: item.asn?.asn,
org: item.asn?.org,
domain: item.asn?.domain,
type: item.asn?.type,
abuseScore: Number(item.asn?.abuser_score?.split(" ")[0]),
},
};
const companyAbuseScoreRaw = item.company?.abuser_score;
const companyAbuseScore =
companyAbuseScoreRaw != null
? Number.parseFloat(companyAbuseScoreRaw)
: undefined;
const asnAbuseScoreRaw = item.asn?.abuser_score;
const asnAbuseScore =
asnAbuseScoreRaw != null
? Number.parseFloat(asnAbuseScoreRaw)
: undefined;
results[ip] = {
company: {
name: item.company?.name,
domain: item.company?.domain,
type: item.company?.type,
abuseScore: companyAbuseScore,
},
asn: {
asn: item.asn?.asn,
org: item.asn?.org,
domain: item.asn?.domain,
type: item.asn?.type,
abuseScore: asnAbuseScore,
},
};
🤖 Prompt for AI Agents
In server/src/db/geolocation/geolocation.ts around lines 206 to 220, the code
calls item.company?.abuser_score?.split(" ")[0] (and the same for item.asn)
which will throw if abuser_score is undefined because it still indexes [0];
change this to safely extract the numeric part by first checking that
abuser_score is a string (or non-null), then splitting and verifying the split
array has an element 0 before passing it to Number (or use a parseInt fallback),
and return a safe default (null or 0) when absent so the lookup does not crash.

Comment on lines 58 to +226
} catch (error) {
console.error("Error fetching location:", error);
return { error: "Failed to fetch location" };
logger.error("Error fetching from IPAPI:", error);
return {};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fall back to the local GeoIP reader on IPAPI failures

When fetch throws (network error, timeout, etc.) we now return an empty map, which discards all geolocation data in cloud environments. Please reuse the existing local fallback so analytics still receive best-effort geo data.

   } catch (error) {
     logger.error("Error fetching from IPAPI:", error);
-    return {};
+    return getLocationFromLocal(ips);
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error) {
console.error("Error fetching location:", error);
return { error: "Failed to fetch location" };
logger.error("Error fetching from IPAPI:", error);
return {};
}
} catch (error) {
logger.error("Error fetching from IPAPI:", error);
return getLocationFromLocal(ips);
}
🤖 Prompt for AI Agents
In server/src/db/geolocation/geolocation.ts around lines 223-226, the catch
block currently logs IPAPI errors and returns an empty object which discards geo
data; instead call and return the existing local GeoIP reader fallback used
elsewhere in this module (reuse the same function or code path that looks up IP
locally), log the error as before, and return the fallback result so analytics
receive best-effort geolocation when IPAPI fails.

…acking utilities

- Added 'region' field to LocationResponse type for improved geolocation data.
- Updated geolocation extraction logic to map 'subdivisions' to 'region'.
- Refactored tracking utilities to utilize the new 'region' field, enhancing data accuracy in tracking functions.
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