gateway: /search endpoint = universal "set as default search engine" fix

Adds handleSearch() to public-gateway.mjs: users add
  https://navigate.st/search?q=%s
as their browser's default search engine and typing bare "silentmode.bch"
in the URL bar (which Chromium and Firefox otherwise send to Google as a
search query, since .bch isn't in ICANN's TLD list) arrives here. Native
BNS TLDs (.bch, .p2p, .bit, .nav) 302 to the relay; anything else falls
through to DuckDuckGo so ordinary search still works.

Live on the VPS gateway at /opt/bns-gateway/public-gateway.mjs.
Nginx now proxies location = /search to 127.0.0.1:8700 as well.

Solves: bare "silentmode.bch" in Chrome/Edge went to Google. It now
resolves to the site via the relay in every browser.
This commit is contained in:
Local Dev 2026-07-30 21:21:01 +02:00
parent 41787851a1
commit eb18108fb1

View file

@ -91,10 +91,45 @@ async function serve(res, name, reqPath, baseHref) {
} catch (e) { res.writeHead(502); res.end("gateway error: " + e.message); }
}
// Native BNS TLDs — never overlap ICANN, so hijacking them here is safe.
// Dual TLDs (.com, .de, .dev) always fall through to search, so a bare
// `github.com` in the URL bar keeps going to the real GitHub, not to a
// BNS registration that happens to share the name.
const NATIVE_TLDS = new Set(["bch", "p2p", "bit", "nav"]);
// Universal "set-as-default-search-engine" shim. Users add
// https://navigate.st/search?q=%s
// as their browser's default search engine; typing `silentmode.bch` in the URL
// bar (which Chromium/Firefox otherwise send to Google as a search query
// because .bch isn't in ICANN's known-TLD list) arrives here. If q looks like
// a native BNS name we 302 to the relay (works in every browser, no local
// daemon needed); otherwise forward to DuckDuckGo so ordinary search still
// works. Even an unregistered native-TLD query goes to the relay so the user
// gets a clear NXDOMAIN page instead of a confusing search detour.
async function handleSearch(url, res) {
const q = (url.searchParams.get("q") || "").trim().toLowerCase();
const bare = q.replace(/^[a-z]+:\/\//, "").split("/")[0];
const m = bare.match(/^([a-z0-9-]+)\.([a-z0-9-]+)$/);
let target;
if (m && NATIVE_TLDS.has(m[2])) {
target = `https://navigate.st/bns/${m[1]}.${m[2]}/`;
} else {
target = `https://duckduckgo.com/?q=${encodeURIComponent(q)}`;
}
res.writeHead(302, { location: target });
res.end();
}
const server = http.createServer(async (req, res) => {
try {
const hostHeader = (req.headers.host || "").split(":")[0].toLowerCase();
// --- smart search endpoint (default-search-engine shim) ---
if (req.url.startsWith("/search")) {
const u = new URL(req.url, "http://x");
return handleSearch(u, res);
}
// --- registrar API: availability of a name ---
// GET /api/name/<name> -> { name, tld, registered, records? }
if (req.url.startsWith("/api/name/")) {