Skip to content

feat: add ISP, organization, usage type and connection type tracking - #490

Merged
Blaumaus merged 4 commits into
mainfrom
feature/network-intelligence-panel
Apr 28, 2026
Merged

Blaumaus merged 4 commits into
mainfrom
feature/network-intelligence-panel

Conversation

@Blaumaus

@Blaumaus Blaumaus commented Mar 20, 2026 •

Copy link
Copy Markdown
Member

Changes

If applicable, please describe what changes were made in this pull request.

Community Edition support

  • Your feature is implemented for the Swetrix Community Edition
  • This PR only updates the Cloud (Enterprise) Edition code (e.g. Paddle webhooks, blog, payouts, etc.)

Database migrations

  • Clickhouse / MySQL migrations added for this PR
  • No table schemas changed in this PR

Documentation

  • You have updated the documentation according to your PR
  • This PR did not change any publicly documented endpoints

Summary by CodeRabbit

  • New Features

    • Added "Network intelligence" across Performance, Traffic, and Errors: view breakdowns by ISP, Organisation, Usage type, and Connection type; new network panel/tabs and filters.
  • Documentation

    • API docs updated to list new network filter columns and response examples.
    • Self‑hosting/config guidance added for enabling network data and DB requirements; README updated to advertise network analytics.

@Blaumaus Blaumaus self-assigned this Mar 20, 2026
@coderabbitai

coderabbitai Bot commented Mar 20, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds network intelligence fields (ISP, organisation, usage type, connection type) to IP enrichment, threads them through controllers, transformers, and services, updates ClickHouse schema/migrations, and exposes UI panels, filters, icons, and documentation for the new dimensions.

Changes

Cohort / File(s) Summary
Backend IP geolocation utils
backend/apps/cloud/src/common/utils.ts, backend/apps/community/src/common/utils.ts
Extended IPDetails with isp, organization, userType, connectionType; getIPDetails now returns these fields (populated from traits or null in fallbacks).
Backend controllers
backend/apps/cloud/src/analytics/analytics.controller.ts, backend/apps/community/src/analytics/analytics.controller.ts
Controllers now destructure the four new IP fields and call transformers using single object-argument shapes with renamed short keys (e.g., cc/rg/rgc/ct, isp/og/ut/ctp).
Backend transformers
backend/apps/cloud/src/analytics/utils/transformers.ts, backend/apps/community/src/analytics/utils/transformers.ts
Refactored transformer signatures to accept a single options object (*TransformerOptions) and return nullable isp, og, ut, ctp fields; many params made optional and normalized to `...
Backend analytics services
backend/apps/cloud/src/analytics/analytics.service.ts, backend/apps/community/src/analytics/analytics.service.ts
Added isp, og, ut, ctp to EXCLUDE_NULL_FOR, causing AND <col> IS NOT NULL to be appended for these columns where applicable.
Backend constants
backend/apps/cloud/src/common/constants.ts, backend/apps/community/src/common/constants.ts
Appended isp, og, ut, ctp to TRAFFIC_COLUMNS, PERFORMANCE_COLUMNS, and ERROR_COLUMNS.
Database migrations & init
backend/migrations/clickhouse/2026_03_19_isp_network.js, backend/migrations/clickhouse/initialise_database.js
Added ClickHouse columns isp, og, ut, ctp (Nullable / LowCardinality / CODEC settings) to analytics, customEV, performance, and errors tables.
Frontend constants & filters
web/app/lib/constants/index.ts, web/app/pages/Project/View/utils/filters.tsx
Added network panel ordering and included isp, og, ut, ctp in filter ordering and valid filter lists.
Frontend UI: panels & tabs
web/app/pages/Project/View/ViewProject.helpers.tsx, web/app/pages/Project/tabs/Traffic/TrafficView.tsx, web/app/pages/Project/tabs/Performance/PerformanceView.tsx, web/app/pages/Project/tabs/Errors/ErrorsView.tsx
Added network panel rendering and tab state for Traffic/Performance/Errors; supports subtabs isp, og, ut, ctp, data mapping and formatting.
Frontend filter UI & i18n
web/app/ui/FilterValueInput.tsx, web/public/locales/en.json
Added icons for isp, og, ut, ctp and English labels: ISP, Organisation, Usage type, Connection type.
Docs & README
docs/..., README.md
Documented “Network intelligence” panels, API filter columns examples, and self-hosting/GeoIP configuration guidance.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client Request
    participant Controller as Analytics Controller
    participant IP as IP Geolocation Service
    participant Transformer as Event Transformer
    participant Service as Analytics Service
    participant DB as ClickHouse DB

    Client->>Controller: send event (with client IP)
    Controller->>IP: getIPDetails(ip)
    IP-->>Controller: {cc, rg, rgc, ct, isp, organization, userType, connectionType}
    Controller->>Transformer: transformEvent({..., cc, rg, rgc, ct, isp, og, ut, ctp})
    Transformer-->>Controller: enrichedEvent
    Controller->>Service: processEvent(enrichedEvent)
    Service->>Service: apply EXCLUDE_NULL_FOR filters (isp, og, ut, ctp, ...)
    Service->>DB: insert/enqueue enrichedEvent
    DB-->>Service: ack
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Rework filters experience #423: Updates frontend filter constants/validation (FILTERS_PANELS_ORDER, ERRORS_FILTERS_PANELS_ORDER) — closely related to the new filter dimensions added here.

Poem

🐰 I hopped through traits on a moonlit night,
Found ISPs and orgs in the soft starlight,
Usage and links in a tidy heap,
Networks awake while the servers sleep,
Data dances — nimble, clever, and bright. 🌐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title accurately and specifically describes the main change: adding ISP, organization, usage type, and connection type tracking to the analytics system.
Description check ✅ Passed The description addresses all key template sections: Changes section is implicitly covered by the title, Community Edition support is properly marked with the Cloud-only option selected, database migrations are confirmed as added, and documentation updates are confirmed completed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/network-intelligence-panel

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.

@Blaumaus
Blaumaus marked this pull request as draft March 20, 2026 00:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/apps/cloud/src/common/utils.ts`:
- Around line 269-307: getGeoDetails currently extracts
ISP/organization/userType/connectionType traits (isp, organization, userType,
connectionType) but the deployed DB-IP City Lite (dbip-free-sync.js) does not
contain those traits so they'll always be null; fix by either switching the sync
script to dbip-commercial-sync.js and wiring the DB-IP API key so the commercial
DB with traits is used, or remove the trait extraction and related return fields
from getGeoDetails (and any upstream consumers) so the function only returns
country, city, region, and regionCode when using the free DB; update
callers/types accordingly (refer to getGeoDetails and the trait variables
isp/organization/userType/connectionType).

In `@web/public/locales/en.json`:
- Around line 1579-1583: Add the missing locale key project.network to the
English locale (web/public/locales/en.json) so ViewProject.helpers.tsx's
t('project.network') resolves; mirror the existing dashboard.network value
(e.g., "Network") to keep labels consistent and prevent fallback to the key
text.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a4491ee-4071-47e4-b63e-3b46abf7c20a

📥 Commits

Reviewing files that changed from the base of the PR and between 50ace4f and f9fcc13.

📒 Files selected for processing (16)
  • backend/apps/cloud/src/analytics/analytics.controller.ts
  • backend/apps/cloud/src/analytics/analytics.service.ts
  • backend/apps/cloud/src/analytics/utils/transformers.ts
  • backend/apps/cloud/src/common/constants.ts
  • backend/apps/cloud/src/common/utils.ts
  • backend/meta/dbip-commercial-sync.js
  • backend/migrations/clickhouse/2026_03_19_isp_network.js
  • backend/migrations/clickhouse/initialise_database.js
  • web/app/lib/constants/index.ts
  • web/app/pages/Project/View/ViewProject.helpers.tsx
  • web/app/pages/Project/View/utils/filters.tsx
  • web/app/pages/Project/tabs/Errors/ErrorsView.tsx
  • web/app/pages/Project/tabs/Performance/PerformanceView.tsx
  • web/app/pages/Project/tabs/Traffic/TrafficView.tsx
  • web/app/ui/FilterValueInput.tsx
  • web/public/locales/en.json

Comment thread backend/apps/cloud/src/common/utils.ts Resolved
Comment thread web/public/locales/en.json Resolved
@Blaumaus
Blaumaus marked this pull request as ready for review April 27, 2026 22:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
backend/apps/community/src/common/utils.ts (1)

1146-1161: Optional: Reuse the local traits variable for the new field reads.

traits is already destructured on line 1146 and used on line 1148 for isHosting. The four new lookups on lines 1158–1161 redundantly traverse data?.traits?.… again — using traits.isp, traits.organization, traits.user_type, traits.connection_type would be slightly more consistent and avoids the extra optional-chaining hops.

♻️ Proposed refactor
-  const isp = data?.traits?.isp || null
-  const organization = data?.traits?.organization || null
-  const userType = data?.traits?.user_type || null
-  const connectionType = data?.traits?.connection_type || null
+  const isp = traits.isp || null
+  const organization = traits.organization || null
+  const userType = traits.user_type || null
+  const connectionType = traits.connection_type || null
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/apps/community/src/common/utils.ts` around lines 1146 - 1161, The new
lookups for isp, organization, userType, and connectionType repeat
optional-chaining into data?.traits; reuse the already-declared local variable
traits instead: replace data?.traits?.isp, data?.traits?.organization,
data?.traits?.user_type, and data?.traits?.connection_type with traits.isp,
traits.organization, traits.user_type, and traits.connection_type respectively
(preserving the null fallback behavior used elsewhere), so the isHosting logic
and subsequent reads all use the same traits reference.
backend/apps/cloud/src/common/utils.ts (1)

283-296: Optional: Reuse the local traits variable for the new field reads.

traits is already extracted on line 283. Lines 293–296 re-traverse data?.traits?.…; using traits.isp / traits.organization / traits.user_type / traits.connection_type would be more consistent with line 285 and slightly cheaper.

♻️ Proposed refactor
-  const isp = data?.traits?.isp || null
-  const organization = data?.traits?.organization || null
-  const userType = data?.traits?.user_type || null
-  const connectionType = data?.traits?.connection_type || null
+  const isp = traits.isp || null
+  const organization = traits.organization || null
+  const userType = traits.user_type || null
+  const connectionType = traits.connection_type || null
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/apps/cloud/src/common/utils.ts` around lines 283 - 296, The code
extracts const traits = data?.traits ?? {} but then redundantly reads
data?.traits for isp, organization, user_type and connection_type; update the
assignments for isp, organization, userType and connectionType to read from the
local traits variable (e.g., const isp = traits.isp || null, const organization
= traits.organization || null, const userType = traits.user_type || null, const
connectionType = traits.connection_type || null) so they mirror the earlier
isHosting/traits usage and avoid re-traversing data.
docs/content/docs/api/stats.mdx (1)

1471-1551: Optional: Update the common request example response to include the new params keys.

The /v1/log example response at the top (lines 220-248) was extended with isp, og, ut, and ctp arrays, but the second example response under "Common request examples" still only shows the legacy keys (ending at "co": []). For consistency between the two example responses for the same endpoint, consider appending empty isp/og/ut/ctp arrays here as well.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/content/docs/api/stats.mdx` around lines 1471 - 1551, The "Common
request examples" response params object is missing the new keys added to the
top /v1/log example; update the second example under "Common request examples"
by adding empty arrays for the new params keys isp, og, ut, and ctp inside the
params object so both examples match; locate the params JSON in the
docs/content/docs/api/stats.mdx example (the object containing
"cc","rg","ct",...,"co") and append isp: [], og: [], ut: [], ctp: [] to that
object.
backend/apps/community/src/analytics/utils/transformers.ts (1)

26-256: Consider refactoring to an options-object signature.

These transformers already had long positional parameter lists, and this PR pushes them further (e.g., trafficTransformer to 26 positional args, customEventTransformer/errorEventTransformer to 27). Inserting four new geo-adjacent parameters (isp, og, ut, ctp) at a specific position relies on every call site being updated in the exact same order — a silent off-by-one (e.g., swapping ut/ctp or shifting isp past ct) would corrupt analytics rows without any type-system protection since all the new params share the same string | null type.

Switching to a single typed parameter object (e.g., trafficTransformer({ psid, profileId, …, isp, og, ut, ctp, meta })) would eliminate this category of bug and make future column additions safer. Not blocking for this PR, but recommended as a follow-up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/apps/community/src/analytics/utils/transformers.ts` around lines 26 -
256, The transformer functions (trafficTransformer, customEventTransformer,
errorEventTransformer, performanceTransformer) use long positional parameter
lists which are error-prone; refactor each to accept a single typed options
object (e.g., trafficTransformer(opts: { psid: string; profileId: string; pid:
string; host?: string | null; ... isp?: string | null; og?: string | null; ut?:
string | null; ctp?: string | null; meta?: Record<string,string> | null })) and
then destructure inside the function, preserving the current null-coalescing
behavior and use of processMetaKV(meta) and dayjs.utc() for created; update all
call sites to pass a single object with named properties to avoid positional
mistakes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/apps/cloud/src/common/utils.ts`:
- Around line 283-296: The code extracts const traits = data?.traits ?? {} but
then redundantly reads data?.traits for isp, organization, user_type and
connection_type; update the assignments for isp, organization, userType and
connectionType to read from the local traits variable (e.g., const isp =
traits.isp || null, const organization = traits.organization || null, const
userType = traits.user_type || null, const connectionType =
traits.connection_type || null) so they mirror the earlier isHosting/traits
usage and avoid re-traversing data.

In `@backend/apps/community/src/analytics/utils/transformers.ts`:
- Around line 26-256: The transformer functions (trafficTransformer,
customEventTransformer, errorEventTransformer, performanceTransformer) use long
positional parameter lists which are error-prone; refactor each to accept a
single typed options object (e.g., trafficTransformer(opts: { psid: string;
profileId: string; pid: string; host?: string | null; ... isp?: string | null;
og?: string | null; ut?: string | null; ctp?: string | null; meta?:
Record<string,string> | null })) and then destructure inside the function,
preserving the current null-coalescing behavior and use of processMetaKV(meta)
and dayjs.utc() for created; update all call sites to pass a single object with
named properties to avoid positional mistakes.

In `@backend/apps/community/src/common/utils.ts`:
- Around line 1146-1161: The new lookups for isp, organization, userType, and
connectionType repeat optional-chaining into data?.traits; reuse the
already-declared local variable traits instead: replace data?.traits?.isp,
data?.traits?.organization, data?.traits?.user_type, and
data?.traits?.connection_type with traits.isp, traits.organization,
traits.user_type, and traits.connection_type respectively (preserving the null
fallback behavior used elsewhere), so the isHosting logic and subsequent reads
all use the same traits reference.

In `@docs/content/docs/api/stats.mdx`:
- Around line 1471-1551: The "Common request examples" response params object is
missing the new keys added to the top /v1/log example; update the second example
under "Common request examples" by adding empty arrays for the new params keys
isp, og, ut, and ctp inside the params object so both examples match; locate the
params JSON in the docs/content/docs/api/stats.mdx example (the object
containing "cc","rg","ct",...,"co") and append isp: [], og: [], ut: [], ctp: []
to that object.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5bdd546d-7bbf-4983-8055-6397f346e062

📥 Commits

Reviewing files that changed from the base of the PR and between f9fcc13 and a1d94d5.

📒 Files selected for processing (14)
  • README.md
  • backend/apps/cloud/src/analytics/analytics.controller.ts
  • backend/apps/cloud/src/analytics/analytics.service.ts
  • backend/apps/cloud/src/common/utils.ts
  • backend/apps/community/src/analytics/analytics.controller.ts
  • backend/apps/community/src/analytics/analytics.service.ts
  • backend/apps/community/src/analytics/utils/transformers.ts
  • backend/apps/community/src/common/constants.ts
  • backend/apps/community/src/common/utils.ts
  • backend/migrations/clickhouse/initialise_database.js
  • docs/content/docs/analytics-dashboard/performance.mdx
  • docs/content/docs/analytics-dashboard/traffic.mdx
  • docs/content/docs/api/stats.mdx
  • docs/content/docs/selfhosting/configuring.mdx
✅ Files skipped from review due to trivial changes (3)
  • docs/content/docs/selfhosting/configuring.mdx
  • README.md
  • backend/migrations/clickhouse/initialise_database.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/cloud/src/analytics/analytics.controller.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
backend/apps/community/src/analytics/utils/transformers.ts (1)

26-53: Align processMetaKV signature with nullable meta contract.

The three transformer option types declare meta?: Record<string, string> | null, but processMetaKV accepts only non-nullable Record<string, string>. While strict mode is not currently enabled in this module's tsconfig, the type mismatch should be resolved for clarity—the implementation already safely handles null and undefined.

♻️ Proposed fix
const processMetaKV = (
-  rawMeta: Record<string, string>,
+  rawMeta?: Record<string, string> | null,
): { 'meta.key': string[] | null; 'meta.value': string[] | null } => {

Applies to: lines 10–24, and all three call sites at 109, 200, 291.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/apps/community/src/analytics/utils/transformers.ts` around lines 26 -
53, The transformer option types (e.g., TrafficTransformerOptions) declare
meta?: Record<string,string> | null but processMetaKV currently accepts only
non-nullable Record<string,string>; update processMetaKV's parameter type to
accept Record<string,string> | null | undefined (or make it optional) and keep
its existing null/undefined-safe logic, then ensure all call sites (the three
transformer functions that pass .meta) compile with the relaxed signature;
reference processMetaKV and the transformer option types (e.g.,
TrafficTransformerOptions) when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/apps/community/src/analytics/utils/transformers.ts`:
- Around line 26-53: The transformer option types (e.g.,
TrafficTransformerOptions) declare meta?: Record<string,string> | null but
processMetaKV currently accepts only non-nullable Record<string,string>; update
processMetaKV's parameter type to accept Record<string,string> | null |
undefined (or make it optional) and keep its existing null/undefined-safe logic,
then ensure all call sites (the three transformer functions that pass .meta)
compile with the relaxed signature; reference processMetaKV and the transformer
option types (e.g., TrafficTransformerOptions) when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 08af363b-7e00-47bf-bc60-a2350d5ec211

📥 Commits

Reviewing files that changed from the base of the PR and between a1d94d5 and efde398.

📒 Files selected for processing (7)
  • backend/apps/cloud/src/analytics/analytics.controller.ts
  • backend/apps/cloud/src/analytics/utils/transformers.ts
  • backend/apps/cloud/src/common/utils.ts
  • backend/apps/community/src/analytics/analytics.controller.ts
  • backend/apps/community/src/analytics/utils/transformers.ts
  • backend/apps/community/src/common/utils.ts
  • docs/content/docs/api/stats.mdx
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/content/docs/api/stats.mdx
  • backend/apps/community/src/common/utils.ts
  • backend/apps/cloud/src/analytics/utils/transformers.ts

@Blaumaus
Blaumaus merged commit e214c29 into main Apr 28, 2026
12 checks passed
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