Skip to content

Deduplicate IP detection logic into shared utility #13

@xinbenlv

Description

@xinbenlv

Original Request

Deduplicate IP detection logic into shared utility

Context: IP address detection (IPv4/IPv6 regex matching for Wikipedia anonymous editors) appears in at least 3 places: packages/server/src/routes/rankedFeed.ts, packages/core/src/composables/useReviewFeed.ts, and potentially other locations. Each implements its own regex. This violates DRY and means bug fixes to the pattern need to be applied in multiple places. The fix should extract this into a shared utility in @doublecheck/core (e.g., core/src/utils/ip.ts with isIPAddress(username: string): boolean) and import it everywhere. This also enables proper unit testing of the IP detection logic in one place.

Agent's Two Cents (could be wrong)

Everything below is the AI agent's best guess based on the current codebase.
Take with a grain of salt — the original request above is the only thing that came from a human.

Problem / Motivation

The same isIPUser() function is copy-pasted in at least two files with identical regex logic for detecting Wikipedia anonymous editors (IPv4 via ^\d{1,3}(\.\d{1,3}){3}$ and IPv6 via ^[0-9a-fA-F:]+$ with colon check). If a bug is found in the pattern — e.g., the IPv6 regex is overly permissive and would match strings like a:b — the fix must be applied in every copy. This is a classic DRY violation that grows worse over time.

Proposed Solution

Create a shared utility module in @doublecheck/core (e.g., packages/core/src/utils/ip.ts) exporting an isIPAddress(username: string): boolean function. Replace all duplicated isIPUser() implementations with imports from this shared module. The server package already depends on core, so the import graph is clean.

Dependencies & Potential Blockers

No major blockers identified. The server package already imports from @doublecheck/core, so adding another import is straightforward. The packages/core/src/utils/ directory does not yet exist and will need to be created, plus a barrel export added to core/src/index.ts.

How to Validate

  • packages/core/src/utils/ip.ts exists and exports isIPAddress()
  • Unit tests in packages/core/src/__tests__/ip.test.ts cover: valid IPv4 (1.2.3.4, 192.168.0.1), valid IPv6 (2001:0db8::1, ::1), non-IP usernames (JohnDoe, Bot123), edge cases (empty string, partial matches like 999.999.999.999)
  • packages/server/src/routes/rankedFeed.ts imports from @doublecheck/core instead of defining its own isIPUser
  • packages/core/src/composables/useReviewFeed.ts imports from the shared utility instead of defining its own isIPUser
  • pnpm build succeeds across all packages
  • Existing tests continue to pass (pnpm test)

Scope Estimate

Small

Key Files/Modules Likely Involved

  • packages/core/src/utils/ip.ts (new — shared utility)
  • packages/core/src/index.ts (add export)
  • packages/core/src/composables/useReviewFeed.ts (replace local isIPUser)
  • packages/server/src/routes/rankedFeed.ts (replace local isIPUser)
  • packages/core/src/__tests__/ip.test.ts (new — unit tests)

Rough Implementation Sketch

  • Create packages/core/src/utils/ip.ts with a single exported isIPAddress() function containing the consolidated IPv4/IPv6 regex logic
  • Add a barrel export from packages/core/src/index.ts
  • In rankedFeed.ts: remove the local isIPUser function, import isIPAddress from @doublecheck/core
  • In useReviewFeed.ts: remove the local isIPUser function, import isIPAddress from @doublecheck/core
  • Write unit tests covering IPv4, IPv6, non-IP strings, and edge cases
  • Consider tightening the IPv6 regex while consolidating (current pattern ^[0-9a-fA-F:]+$ is loose)

Open Questions

  • Should the IPv6 regex be tightened during this refactor, or kept identical to the current (loose) pattern and improved in a follow-up? The current regex accepts strings like a:b which are not valid IPv6 addresses.
  • Should the function name be isIPAddress (as suggested) or keep the existing isIPUser convention? The former is more precise about what it checks; the latter matches existing call sites.
  • The leaderboard route (packages/server/src/routes/leaderboard.ts) filters anonymous users via a MongoDB $match against the literal string "anonymous" — should this also be updated to use IP detection, or is it a different concern?

Potential Risks or Gotchas

  • The IPv6 regex is quite permissive — any string of hex chars and colons with at least one colon matches. Tightening it during this refactor could change filtering behavior if any real usernames happen to match the loose pattern but not a strict one. Recommend keeping the existing behavior first, then tightening in a separate PR with explicit test coverage.
  • The userscript package has its own detectIdentity() mechanism for anonymous users (checking mw.config.get("wgUserIsAnon")) which is a different concern (MediaWiki runtime detection vs. username-string parsing) — it should NOT use this utility.

Metadata

Metadata

Assignees

No one assigned

    Labels

    AgentsCanDoSuitable for autonomous agent pickupenhancementNew feature or requestp3Low priority

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions