fix: smart routing in Browse — enrichment filters use /api/enriched, discovery uses /api/domains

Root cause: loadDomains() always hit /api/domains (DuckDB 72M rows) and filtered
niche/site_type/prescreen_status client-side on a random page of 100 domains —
virtually none had been classified, so Live+Beauty+Ecommerce always returned 0.

- loadDomains() now routes to /api/enriched when any enrichment filter is active
  (prescreen_status, niche, site_type, country) — all filters are server-side SQLite
- Falls back to /api/domains only when no enrichment filters are set (discovery mode)
- alpha_only and no_sld supported in both modes:
  - DuckDB: existing regex support
  - SQLite: LIKE patterns (no hyphens/digits) + dot-count (no SLD)
- Add alpha_only/no_sld params to /api/enriched endpoint and get_enriched()
- Fix stale d.classified reference in prescreenOne toast

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 08:53:54 +02:00
parent daccb99a0c
commit 2f0959b8e8
3 changed files with 33 additions and 10 deletions

View File

@@ -492,16 +492,24 @@ function app() {
if (this.f.tld) p.set('tld', this.f.tld.trim());
if (this.f.alpha_only) p.set('alpha_only', 'true');
if (this.f.no_sld) p.set('no_sld', 'true');
const d = await fetch('/api/domains?' + p).then(r=>r.json());
const hasEnrichFilter = this.f.prescreen_status || this.f.niche || this.f.site_type || this.f.country;
let endpoint;
if (hasEnrichFilter) {
// Enrichment filters require the SQLite table — add them server-side
if (this.f.prescreen_status) p.set('prescreen_status', this.f.prescreen_status);
if (this.f.niche) p.set('niche', this.f.niche);
if (this.f.site_type) p.set('site_type', this.f.site_type);
if (this.f.country) p.set('country', this.f.country.trim().toUpperCase());
endpoint = '/api/enriched';
} else {
// Discovery mode: search full 72M DuckDB index (e.g. "Not checked" keyword search)
endpoint = '/api/domains';
}
const d = await fetch(endpoint + '?' + p).then(r=>r.json());
this.domainsTotal = d.total || 0;
let rows = d.results || [];
// Client-side filters (same pattern as main DomGod)
if (this.f.prescreen_status === 'none') rows = rows.filter(r => !r.prescreen_status);
else if (this.f.prescreen_status) rows = rows.filter(r => r.prescreen_status === this.f.prescreen_status);
if (this.f.niche) rows = rows.filter(r => r.niche === this.f.niche);
if (this.f.site_type) rows = rows.filter(r => r.site_type === this.f.site_type);
if (this.f.country) rows = rows.filter(r => r.ip_country === this.f.country.trim().toUpperCase());
this.domains = rows;
this.domains = d.results || [];
} catch(e) { this.notify('Failed to load: '+e.message, 'error'); }
finally { this.loading = false; }
},
@@ -596,7 +604,7 @@ function app() {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({domains:[domain]}),
}).then(r=>r.json());
this.notify(`${domain}: ${d.live?'live':'dead/parked'}, classified: ${d.classified}`, 'success');
this.notify(`${domain}: ${d.live?'live':'dead/parked'}${d.classifying?' · classifying in background':''}`, 'success');
await this.loadDomains();
} catch(e) { this.notify('Failed: '+e.message, 'error'); }
},