Skip to content

[pull] main from jason5ng32:main#107

Merged
pull[bot] merged 19 commits into
Cosr-Backup:mainfrom
jason5ng32:main
May 13, 2026
Merged

[pull] main from jason5ng32:main#107
pull[bot] merged 19 commits into
Cosr-Backup:mainfrom
jason5ng32:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 13, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

jason5ng32 and others added 19 commits April 25, 2026 18:24
…tatic assets

Service-worker offline caching had no real value (every detection in this
app needs the network) and conflicted with Cloudflare challenge pages —
the SW would NetworkFirst-timeout and serve a stale index.html, then the
hashed chunk requests came back as a wall of 403s from CF.

Replace with conventional HTTP cache headers in frontend-server.js:
  - dist/assets/** + dist/fonts/**  → 1y immutable (content-addressed)
  - top-level images                → 24h
  - index.html + manifest.webmanifest → no-store (so deploys propagate
                                                  cleanly to hashed chunks)
  - everything else                 → 1h

Existing prod clients still have the old SW + ~5 MB precache installed,
so unregister-service-worker.js does a one-shot cleanup on first load
after this deploy: unregister all SW registrations, drop every cache.
Safe to delete the file and its main.js call after a few release cycles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Most /api endpoints are caller-specific (e.g. /api/ipinfo without ?ip
reads the *caller's* IP, /api/getuserinfo returns the authenticated
user's profile). Without explicit headers, a stray Cloudflare cache rule
could serve user A's response to user B.

Add a one-line middleware on /api/* that defaults every response to
no-store, mounted before requireReferer so even rejected 403/404/429
responses carry the header. Pure-function handlers (whois / macchecker /
cfradar / map / configs) can override later with public, max-age=… if
edge caching is desired.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Rename pref connectivityAutoRefresh → connectivityMultipleTests across
  defaults, tests, locales (en/zh/fr/tr), Preferences, and ConnectivityTest.
- Defer the Congrats/OhNo toast until every round finishes; aggregate the
  verdict via "ever-succeeded across all rounds" (mintime > 0) so a target
  reachable in any later round still counts as connected. Sticky
  alertToShow latch prevents interval ticks from suppressing it.
- Preserve best-of-N face/text/ms — a later failed round no longer
  downgrades a card that already succeeded.
- Add per-card progress dots: one slot per round, bootstrap-only writer so
  manual operations leave the historical snapshot alone. Head-of-queue
  pulses; pulse suppressed once the user takes over.
- Trim verbose comments throughout the component.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New /api/asn-history handler queries RIPEstat routing-history for an IP
and enriches each historical ASN with its holder name via as-overview
(parallel, best-effort — org=null on timeout, ASN timeline still ships).
Filters out leak-grade prefixes (< /8 v4, < /19 v6) and low-visibility
announcements (< 30 full-feed peers) before fetching org names.

UI: ASN row in IpDetailPanel now has two view-toggles (Info / History)
sharing one Collapsible; at most one open at a time. Session-cached
per IP. Locale keys added in en/zh/fr/tr; bumps version to 6.2.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce a cacheable(maxAge) middleware factory in backend-server.js
that hooks res.json to attach Cache-Control: public, max-age=N — but
only on 2xx responses, so Cloudflare's edge never caches 4xx/5xx.

Wire it on the routes whose upstreams refresh slowly: cfradar /
asn-history / whois / maxmind / macchecker plus the third-party IP
geolocation handlers (ipinfo / ipapicom / ipsb / ipapiis / ip2location).
Auth-context handlers (ipchecking, invisibility, dns-leak-test/session,
getuserinfo, updateuserachievement) remain on the global no-store
default — their caching belongs at the upstream that owns the auth.

Also trim stale "what" comments throughout backend-server.js and
document the new pattern in api/AGENTS.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
/api/asn-history previously took ?ip=, producing a unique CF cache key
per IP — wasteful since BGP routes are announced at /24 (v4) and /48
(v6) at minimum, so every IP in the same prefix has identical routing
history. Frontend now quantizes the user's IP to its DFZ-floor prefix
via toBgpPrefix() and hits /api/asn-history?prefix=...; CF can dedupe
256 v4 addresses (or a /48's worth of v6) onto one edge cache entry.

Backend swaps requireValidIP() for a new requireValidPrefix() guard
that validates any well-formed CIDR. RIPEstat returns identical
results whether queried by IP or its covering prefix (verified
empirically), so the upstream call is unchanged in shape.

Shared via common/bgp-prefix.js with a frontend re-export at
frontend/utils/bgp-prefix.js, matching the valid-ip.js pattern. 50
new tests cover v4/v6 quantization (including various IPv6 shorthand
forms, mixed case, leading-zero normalization) and the prefix guard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A dozen components already statically import @iconify/vue, so wrapping
addCollection in a dynamic import() achieved nothing — Rollup couldn't
split it into a separate chunk. Promote @iconify/vue to a static import
and keep only the heavy circle-flags JSON (~500 kB) lazy, which is the
actual first-paint optimization.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The fallback loop in QueryIP's fetchIPForModal silently relied on a
JS truthy-check bug — `if (sourceID && source.id !== sourceID)` only
falls through to other sources when sourceID is 0 (which happens to
be IPCheck.ing). For any other preferred source (ipinfo, ip-api,
etc.), the filter activates and the loop tries exactly one source,
so a 5xx from that source left the spinner running forever with no
visible error.

Replace with the same cyclic walk IpInfos.fetchIPDetails uses:
findIndex by id, walk (currentIdx + 1) % sources.length on failure,
attempts < sources.length termination. 200 responses with all-"N/A"
fields are treated as valid output (upstream is healthy, just no
data for this IP) rather than triggering fallback.

When every source genuinely fails, surface a new ipcheck.NoData
error string across en/zh/fr/tr.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Keep the selected ASN panel content mounted separately from the Collapsible open state so the close height animation can complete without a late layout jump.

Co-authored-by: Codex <codex@openai.com>
@pull pull Bot locked and limited conversation to collaborators May 13, 2026
@pull pull Bot added the ⤵️ pull label May 13, 2026
@pull pull Bot merged commit 85d5034 into Cosr-Backup:main May 13, 2026
3 of 4 checks passed
@4everland 4everland Bot requested a deployment to production May 13, 2026 06:27 Abandoned
@pull pull Bot had a problem deploying to production May 13, 2026 06:27 Failure
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant