A pass over every file the branch touched, after the iOS animation hunt
left tries and reverts behind it. Removed what nothing reaches: the
view-transition rule for a transition no code starts, an unreachable
guard in the transition hook, a no-op KeepAlive key branch, a sign-in
guard the menu already enforces, usage-dialog wrappers that only
forwarded to the store, a duplicated modifier-click check and card
filter (isPlainClick / listedTools), a test that hand-rolled
listedTools, a drawer-era fixture parameter. auth-flow.js folded into
auth-redirect.js. Comments cut to what the code cannot say itself —
file headers of a few lines, the measured iOS reasons, non-obvious
orderings — and stale ones fixed (Nav under KeepAlive, Home.vue
reusing DEFAULT_META, "from Nav", enabledCards). AGENTS.md sections
tightened to the code as it is now.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
VITE_FIREBASE_AUTH_DOMAIN stays the shared sign-in domain every browser
uses; the new optional VITE_FIREBASE_PWA_AUTH_DOMAIN is the domain the
installed PWA initialises Firebase with — the site's own host, served
by the /__/auth proxy, so the redirect handler runs inside the PWA
(utils/auth-domain.js). GitHub's OAuth app has one callback, on the
shared domain, so when the PWA domain differs the account menu offers
Google only. .env.example documents both and the console-side steps.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
The installed iOS PWA opens an out-of-scope authDomain in the system's
in-app browser view, whose storage the PWA's own webview cannot read,
so a redirect sign-in completed there never reached the app. The
frontend server and the Vite dev server now proxy /__/auth/* to
https://<project>.firebaseapp.com (common/firebase-auth-proxy.js),
mounted only when VITE_FIREBASE_PROJECT_ID is set, so with
VITE_FIREBASE_AUTH_DOMAIN set to the site's host the handler runs
in-scope, inside the PWA itself. .env.example documents the three keys
and the console-side requirements (authorized domain, OAuth callback at
https://<host>/__/auth/handler).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
common/ had become the backend's library: 30 of its 41 modules were
imported by nothing in the frontend, so the name no longer said what
belonged there. It now holds only what the frontend or the build imports
too; backend-only code moved to server/, flat except for datasets/ (the
dataset engine and its readers). The *-db/ dataset directories stay in
common/: self-hosting docs and Docker volumes mount those paths.
frontend/utils/ keeps its app-wide modules at the root and gains ip/
(IP / ASN shaping and validation), report/ (the report pipeline) and
features/ (the pure layer of a single feature). Paths only — no logic
changes. The Dockerfile copies server/, and the AGENTS.md files say where
a new module goes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Connectivity grid's add tile now opens a tabbed dialog: import one
of 11 curated lists (Iran / Russia / China country packs + AI, social,
streaming, gaming, developer, cloud, crypto, news themes — 130 sites)
or hand-add a custom test. Imported members materialize into the
customConnectivityTargets preference tagged with their listId, so cards
are individually removable and a list can be removed as a whole from
the dialog; hostname dedupe and a shared 60-target cap apply on import.
Cards (built-in included) now show real site favicons served from
committed 64px PNGs under public/favicons/ — same-origin, so icons
render even when the tested site is blocked for the visitor; a data
test enforces one PNG per member, and the static server caches the
directory for 30 days (a changed icon ships under a new id/filename).
Also, since the section was reworked in the same change:
- multi-round mode now runs on manual/section refreshes too — it was
boot-only, so visitors with connectivity auto-run off could never
trigger rounds at all
- shared report caps grow 24 → 72 targets so reports carry every
tested target instead of a silently truncated subset
- dead ServiceStatusBanner leftovers (logic + locale keys) removed;
the banner UI itself was retired earlier
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Make the /tools/:slug pages production-servable and SEO-addressable.
- frontend-server.js: add an SPA history fallback after the static layer — GET
navigations (Accept: text/html, no file extension) get index.html so a hard
load / new tab of a client route resolves. Real assets and /api are untouched;
missing hashed chunks still 404 rather than returning an HTML body.
- New use-document-meta composable: client-side per-route <title> / description
/ canonical / Open Graph. StandaloneTool sets localized per-tool values + a
self-referential canonical; Home resets to the index.html homepage defaults
(so SPA-navigating back from a tool page restores the head). No SSR — applied
at runtime, picked up by the browser tab and JS-rendering crawlers.
Wraps up the standalone-pages work (drawer + standalone now fully co-exist,
dev / preview / prod all serve the routes).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Replace all backend console.* with a shared pino singleton at
common/logger.js. Startup-style lines get emoji prefixes (🚀 listening,
📦 maxmind ready, 📥 downloading, 🗓️ schedule, ⚠️ warning, ❌ failure);
per-handler / per-request logs stay plain.
Three .env-driven knobs, all without NODE_ENV:
- LOG_LEVEL (default info) — pino level filter
- LOG_FORMAT (default pretty) — "json" for log shippers
- LOG_HTTP (default off) — pino-http on /api/* gated behind this flag
so pm2 logs don't bloat with 2xx/3xx request lines on every deploy;
when enabled, 4xx log as warn and 5xx as error
logger.js calls dotenv.config({ quiet: true }) itself because ES module
imports are hoisted above backend-server.js's dotenv call — without that
LOG_LEVEL from .env was silently ignored.
Fixes in common/rdap.js:
- err: res.status → status: res.status (pino's err serializer expects
an Error object, not a number)
- broken single-quoted template literal in "No RDAP endpoint" message
- demote "No RDAP endpoint" and "Domain not found" from error to warn
(they're expected outcomes for user input, not server failures)
README (en/zh/fr/tr) + .env.example + AGENTS.md (root and api/) all
updated to document the three new vars and the "no console.* in backend"
convention.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>