Skip to content

feat(devices): store request country code#2646

Merged
riderx merged 2 commits into
mainfrom
codex/device-country-code
Jul 9, 2026
Merged

feat(devices): store request country code#2646
riderx merged 2 commits into
mainfrom
codex/device-country-code

Conversation

@riderx

@riderx riderx commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

  • add a nullable device country_code column for the latest request country
  • read Cloudflare cf.country in backend stats, persist it in Supabase/Analytics Engine device info, and expose it in public device responses
  • show the country code in the device information panel

Screenshot

Device country code row

Test plan

  • bun run lint (passes, existing admin dashboard warnings remain)
  • bun run lint:backend
  • bunx vitest run tests/device_comparison.test.ts tests/cloudflare-device-pagination.unit.test.ts tests/device-update-format.unit.test.ts
  • bun run typecheck
  • CI: Run tests, Playwright, backend shards, Cloudflare Workers shards, CodeQL, SonarCloud, Socket, Cursor, and CodeRabbit checks passed

Checklist

  • Country code is stored only from backend request context for device update traffic
  • Manual dashboard device creation does not store the operator country
  • Device info UI shows the value when present
  • WebP screenshot attached for the visual change
  • Review comments addressed and resolved

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_code on 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_code column (migration + generated types) is written when device stats are recorded. createStatsDevices reads cf.country, normalizes it via normalizeDeviceCountryCode, and persists through Supabase upserts and Analytics Engine blob10. Public device responses and filterDeviceKeys now include country_code. Manual create_device passes { includeRequestCountry: false } so injected devices are not tagged with the dashboard operator’s country.

Reads and UI: Cloudflare device reads only aggregate blob10 when fetching by explicit deviceIds (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

    • Device details now show the device’s country when available.
    • Device data can now store and carry a country code alongside existing device information.
  • Bug Fixes

    • Country information is now preserved and returned consistently across device views and updates.
    • Country codes are normalized before saving, helping keep values consistent and valid.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a country_code field for devices end-to-end: a new nullable column with migration, generated type updates, a normalization helper, propagation through Cloudflare Analytics Engine and Supabase write/read paths, inclusion in public API responses, display in the device details UI, and corresponding test updates.

Changes

Device country_code feature

Layer / File(s) Summary
Database schema and generated types
supabase/migrations/20260709131018_add_device_country_code.sql, src/types/supabase.types.ts, supabase/functions/_backend/utils/supabase.types.ts
Adds nullable country_code character varying(2) column to devices table with a documenting comment, and updates generated Row/Insert/Update typings in both frontend and backend type files.
Country code normalization and comparison logic
supabase/functions/_backend/utils/deviceComparison.ts, tests/device_comparison.test.ts
Adds exported normalizeDeviceCountryCode helper validating two-letter codes, extends DeviceComparable/DeviceExistingRowLike types, integrates normalization into toComparableDevice/toComparableExisting/buildNormalizedDeviceForWrite, and adds tests covering normalization and change-detection behavior.
Stats ingestion and storage writes
supabase/functions/_backend/utils/stats.ts, supabase/functions/_backend/utils/cloudflare.ts, supabase/functions/_backend/utils/supabase.ts
createStatsDevices derives country_code from the Cloudflare request context and passes an enriched device object to both trackDevicesCF (Analytics Engine blob) and trackDevicesSB (Supabase select/upsert), persisting the value in both storage backends.
Cloudflare Analytics Engine read path
supabase/functions/_backend/utils/cloudflare.ts, tests/cloudflare-device-pagination.unit.test.ts
buildReadDevicesCFQuery conditionally projects country_code via blob10 only when deviceIds are provided, readDevicesCF maps the returned value, and unit tests assert the updated query shape.
Public API response and device details UI
supabase/functions/_backend/public/device/get.ts, src/pages/app/[app].device.[device].vue, tests/device-update-format.unit.test.ts
filterDeviceKeys now preserves country_code in public device responses, the device details page renders a new conditional InfoRow for the country, and tests are updated with fixtures and assertions covering the field.

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
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Title check ✅ Passed The title is concise and accurately summarizes the main change: storing the request country code for devices.
Description check ✅ Passed The description includes the required Summary, Test plan, Screenshot, and Checklist sections and covers the PR changes clearly.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Warning

Review ran into problems

🔥 Problems

Repository analysis: Couldn't refresh Cap-go/capgo clone - clone failed: Clone operation failed: Stream initialization permanently failed: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


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

@codspeed-hq

codspeed-hq Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will degrade performance by 27.77%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 1 regressed benchmark
✅ 42 untouched benchmarks
⏩ 2 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

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)

Open in CodSpeed

Footnotes

  1. 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.

@riderx riderx marked this pull request as ready for review July 9, 2026 14:23

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ 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.

Comment thread supabase/functions/_backend/utils/cloudflare.ts
Comment thread supabase/functions/_backend/utils/stats.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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_code typed as required but conditionally absent from the query.

DeviceInfoCF.country_code is declared as string, but buildReadDevicesCFQuery only projects blob10 AS country_code when includeCountryCode is true (line 829/849). For the default listing path the column isn't selected at all, so row.country_code will actually be undefined at 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

📥 Commits

Reviewing files that changed from the base of the PR and between a719a16 and 19291f5.

📒 Files selected for processing (13)
  • .github/pr-screenshots/device-country-code.webp
  • src/pages/app/[app].device.[device].vue
  • src/types/supabase.types.ts
  • supabase/functions/_backend/public/device/get.ts
  • supabase/functions/_backend/utils/cloudflare.ts
  • supabase/functions/_backend/utils/deviceComparison.ts
  • supabase/functions/_backend/utils/stats.ts
  • supabase/functions/_backend/utils/supabase.ts
  • supabase/functions/_backend/utils/supabase.types.ts
  • supabase/migrations/20260709131018_add_device_country_code.sql
  • tests/cloudflare-device-pagination.unit.test.ts
  • tests/device-update-format.unit.test.ts
  • tests/device_comparison.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread supabase/functions/_backend/utils/stats.ts Outdated
Comment thread tests/cloudflare-device-pagination.unit.test.ts Outdated
Comment thread tests/device_comparison.test.ts Outdated

@cursor cursor 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.

Stale comment

Risk: medium. Not approving: Cursor Bugbot finished with status skipping and reported 2 unresolved findings (country code overwrite via empty blob10, dashboard create using operator edge country). Human review is needed before merge.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@cursor cursor Bot requested review from Dalanir and WcaleNieWolny July 9, 2026 14:31

@cursor cursor 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.

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@sonarqubecloud

sonarqubecloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

@cursor cursor 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.

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor cursor 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.

Risk: medium. Approved: Cursor Bugbot passed on the latest commit with no new findings, and the earlier country-code issues are resolved. WcaleNieWolny and Dalanir are already assigned as reviewers.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@riderx riderx merged commit 26aede9 into main Jul 9, 2026
67 checks passed
@riderx riderx deleted the codex/device-country-code branch July 9, 2026 16:17
riderx added a commit that referenced this pull request Jul 10, 2026
…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>
riderx added a commit that referenced this pull request Jul 10, 2026
…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>
riderx added a commit that referenced this pull request Jul 11, 2026
…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>
riderx added a commit that referenced this pull request Jul 11, 2026
…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>
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