feat(devices): store request country code#2646
Conversation
📝 WalkthroughWalkthroughAdds a ChangesDevice country_code feature
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Device
participant createStatsDevices
participant trackDevicesCF as Analytics Engine
participant trackDevicesSB as Supabase
Device->>createStatsDevices: send device update (with cf.country)
createStatsDevices->>createStatsDevices: normalizeDeviceCountryCode(cf.country)
createStatsDevices->>Analytics Engine: write deviceWithCountry (workerd)
createStatsDevices->>Supabase: upsert deviceWithCountry (fallback)
Analytics Engine-->>createStatsDevices: ack
Supabase-->>createStatsDevices: ack
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Warning Review ran into problems🔥 ProblemsRepository analysis: Couldn't refresh Comment |
Merging this PR will degrade performance by 27.77%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | /updates manifest response with metadata |
87.1 µs | 120.6 µs | -27.77% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing codex/device-country-code (a40bffe) with main (a719a16)
Footnotes
-
2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 19291f5. Configure here.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
supabase/functions/_backend/utils/cloudflare.ts (1)
743-758: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
country_codetyped as required but conditionally absent from the query.
DeviceInfoCF.country_codeis declared asstring, butbuildReadDevicesCFQueryonly projectsblob10 AS country_codewhenincludeCountryCodeis true (line 829/849). For the default listing path the column isn't selected at all, sorow.country_codewill actually beundefinedat runtime, contradicting the type. The mapping at line 921 (row.country_code || null) happens to guard against this safely today, but the type should reflect reality to avoid future misuse.♻️ Proposed fix
- country_code: string + country_code?: string🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@supabase/functions/_backend/utils/cloudflare.ts` around lines 743 - 758, DeviceInfoCF.country_code is declared as always present, but buildReadDevicesCFQuery only selects it when includeCountryCode is enabled, so the type does not match the runtime shape. Update the DeviceInfoCF interface in cloudflare.ts so country_code is optional or nullable, and make sure the downstream mapping in the row-to-device conversion that uses row.country_code remains compatible with the adjusted type.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@supabase/functions/_backend/utils/stats.ts`:
- Around line 135-151: The Analytics Engine path in trackDevicesCF is
overwriting the last known country_code with an empty value when cf.country is
missing or invalid. Update the write logic so deviceWithCountry preserves the
previous non-blank country_code instead of emitting blank values, or adjust the
latest-country selection to ignore empty country codes; use the existing
trackDevicesCF and normalizeDeviceCountryCode flow to locate the fix.
In `@tests/cloudflare-device-pagination.unit.test.ts`:
- Around line 71-81: Add a test case for the buildReadDevicesCFQuery path that
uses customIdMode: true without deviceIds to make the intended gating explicit.
The current coverage in tests/cloudflare-device-pagination.unit.test.ts only
checks the no-deviceIds and deviceIds-present branches; extend the existing
buildReadDevicesCFQuery assertions so the new case verifies blob10 is still
omitted when deviceIds is absent, even in customIdMode.
In `@tests/device_comparison.test.ts`:
- Around line 287-309: This pure helper test in the `hasComparableDeviceChanged`
and `buildNormalizedDeviceForWrite` block does not touch shared state, so switch
the `it` case to `it.concurrent()` to follow the repo’s test guideline. Keep the
same assertions and fixture setup, just update the test declaration so this
comparison-only case can run in parallel safely.
---
Outside diff comments:
In `@supabase/functions/_backend/utils/cloudflare.ts`:
- Around line 743-758: DeviceInfoCF.country_code is declared as always present,
but buildReadDevicesCFQuery only selects it when includeCountryCode is enabled,
so the type does not match the runtime shape. Update the DeviceInfoCF interface
in cloudflare.ts so country_code is optional or nullable, and make sure the
downstream mapping in the row-to-device conversion that uses row.country_code
remains compatible with the adjusted type.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b57a7abc-fe8b-4742-9322-8d2fba82b929
📒 Files selected for processing (13)
.github/pr-screenshots/device-country-code.webpsrc/pages/app/[app].device.[device].vuesrc/types/supabase.types.tssupabase/functions/_backend/public/device/get.tssupabase/functions/_backend/utils/cloudflare.tssupabase/functions/_backend/utils/deviceComparison.tssupabase/functions/_backend/utils/stats.tssupabase/functions/_backend/utils/supabase.tssupabase/functions/_backend/utils/supabase.types.tssupabase/migrations/20260709131018_add_device_country_code.sqltests/cloudflare-device-pagination.unit.test.tstests/device-update-format.unit.test.tstests/device_comparison.test.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual)
There was a problem hiding this comment.
Stale comment
Risk: medium. Not approving: Cursor Bugbot finished with status
skippingand reported 2 unresolved findings (country code overwrite via emptyblob10, dashboard create using operator edge country). Human review is needed before merge.Sent by Cursor Approval Agent: Pull Request Approver
There was a problem hiding this comment.
Stale comment
Risk: medium. Cursor Bugbot finished with a skipped status, so the required automated review did not complete successfully. Human review is needed for the devices migration and stats hot-path changes; assigned WcaleNieWolny and Dalanir.
Sent by Cursor Approval Agent: Pull Request Approver External
|
There was a problem hiding this comment.
Risk: medium. Not approving because this exceeds the low-risk approval threshold (device stats hot path, DB migration, and Analytics Engine schema changes). Cursor Bugbot passed with its two prior findings resolved; human review is still needed—Dalanir and WcaleNieWolny are already assigned.
Sent by Cursor Approval Agent: Pull Request Approver External
…ce types - unknown apps (Capgo removed, misconfigured open-source installs) are the eternal hot path: cache the negative owner result for UPDATES_CACHE_NEGATIVE_TTL_SECONDS (default 600s) instead of the 60s payload TTL, so they cost at most one database query per colo per 10 minutes; safe because the apps INSERT trigger bumps the token the moment an app is created - add missing devices.country_code to both generated supabase.types.ts (backend + frontend): main's country_code feature (#2646/#2659) landed without the regen, breaking typecheck on every PR Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ce types - unknown apps (Capgo removed, misconfigured open-source installs) are the eternal hot path: cache the negative owner result for UPDATES_CACHE_NEGATIVE_TTL_SECONDS (default 600s) instead of the 60s payload TTL, so they cost at most one database query per colo per 10 minutes; safe because the apps INSERT trigger bumps the token the moment an app is created - add missing devices.country_code to both generated supabase.types.ts (backend + frontend): main's country_code feature (#2646/#2659) landed without the regen, breaking typecheck on every PR Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ce types - unknown apps (Capgo removed, misconfigured open-source installs) are the eternal hot path: cache the negative owner result for UPDATES_CACHE_NEGATIVE_TTL_SECONDS (default 600s) instead of the 60s payload TTL, so they cost at most one database query per colo per 10 minutes; safe because the apps INSERT trigger bumps the token the moment an app is created - add missing devices.country_code to both generated supabase.types.ts (backend + frontend): main's country_code feature (#2646/#2659) landed without the regen, breaking typecheck on every PR Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ce types - unknown apps (Capgo removed, misconfigured open-source installs) are the eternal hot path: cache the negative owner result for UPDATES_CACHE_NEGATIVE_TTL_SECONDS (default 600s) instead of the 60s payload TTL, so they cost at most one database query per colo per 10 minutes; safe because the apps INSERT trigger bumps the token the moment an app is created - add missing devices.country_code to both generated supabase.types.ts (backend + frontend): main's country_code feature (#2646/#2659) landed without the regen, breaking typecheck on every PR Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>






Summary
Screenshot
Test plan
Checklist
Note
Medium Risk
Touches device write/read paths across Postgres, Analytics Engine schema (new blob), and public API fields; behavior is gated and tested but mis-deployed migrations or CF query changes could affect device detail accuracy.
Overview
Adds
country_codeon devices so the latest ISO 3166-1 alpha-2 country from Cloudflare on update traffic can be stored and shown.Persistence and API: A nullable
devices.country_codecolumn (migration + generated types) is written when device stats are recorded.createStatsDevicesreadscf.country, normalizes it vianormalizeDeviceCountryCode, and persists through Supabase upserts and Analytics Engineblob10. Public device responses andfilterDeviceKeysnow includecountry_code. Manualcreate_devicepasses{ includeRequestCountry: false }so injected devices are not tagged with the dashboard operator’s country.Reads and UI: Cloudflare device reads only aggregate
blob10when fetching by explicitdeviceIds(e.g. single-device detail), not on bulk listings—matching the pattern used for install source. The app device detail page shows a country row when the value is present.Tests cover normalization, comparison/write paths, public filtering, and CF query shape for country vs listing modes.
Reviewed by Cursor Bugbot for commit a40bffe. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
Bug Fixes