← back to Fabricwallpaper
facets: filter by q+aesthetic; drop vendor exposure from public APIs
2002896b6498c3e914156f676c8ea915533f0eda · 2026-05-25 21:03:02 -0700 · Steve Abrams
Files touched
Diff
commit 2002896b6498c3e914156f676c8ea915533f0eda
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon May 25 21:03:02 2026 -0700
facets: filter by q+aesthetic; drop vendor exposure from public APIs
---
server.js | 36 ++++++++++++++++++++++--------------
1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/server.js b/server.js
index b12fb88..00328d8 100644
--- a/server.js
+++ b/server.js
@@ -160,22 +160,27 @@ for (const name of CLEAN_PAGES) {
// /about has no dedicated page — the "short history" page is the closest fit.
app.get('/about', (req, res) => res.sendFile(path.join(__dirname, 'public', 'history.html')));
-app.get('/api/products', (req, res) => {
- const { q, aesthetic, vendor, page = 1, limit = 24, sort = 'newest'} = req.query;
- let list = PRODUCTS_NICHE;
+// Apply customer-facing query filters (q + aesthetic). The `vendor` filter is
+// intentionally NOT honoured — vendor identity is never exposed on this site.
+function applyFilters(list, { q, aesthetic }) {
+ let out = list;
if (q) {
- const needle = q.toLowerCase();
- list = list.filter(p => (p.title || '').toLowerCase().includes(needle) || (p.tags || []).some(t => t.toLowerCase().includes(needle)));
+ const needle = String(q).toLowerCase();
+ out = out.filter(p => (p.title || '').toLowerCase().includes(needle) || (p.tags || []).some(t => t.toLowerCase().includes(needle)));
}
- if (aesthetic && aesthetic !== 'all') list = list.filter(p => p.aesthetic === aesthetic);
- if (vendor && vendor !== 'all') list = list.filter(p => p.vendor === vendor);
+ if (aesthetic && aesthetic !== 'all') out = out.filter(p => p.aesthetic === aesthetic);
+ return out;
+}
+
+app.get('/api/products', (req, res) => {
+ const { q, aesthetic, page = 1, limit = 24, sort = 'newest'} = req.query;
+ let list = applyFilters(PRODUCTS_NICHE, { q, aesthetic });
list = sortProducts(list, sort);
const total = list.length;
const pageNum = Math.max(1, parseInt(page) || 1);
const lim = Math.min(60, parseInt(limit) || 24);
const start = (pageNum - 1) * lim;
res.json({ total, page: pageNum, limit: lim, pages: Math.ceil(total/lim), sort, items: list.slice(start, start + lim) });
- // (replaced) old: pages: Math.ceil(total / lim), items: list.slice(start, start + lim) });
});
app.get('/api/sliders', (req, res) => {
@@ -187,13 +192,16 @@ app.get('/api/sliders', (req, res) => {
res.json({ rails: out });
});
+// Facet counts reflect the current filtered list (q + optionally aesthetic),
+// so chip badges agree with what the grid is actually showing. When no
+// filters are passed, this matches the unfiltered catalog. `vendors` is
+// intentionally NOT emitted — vendor identity is never exposed publicly.
app.get('/api/facets', (req, res) => {
- const aesthetics = {}; const vendors = {};
- for (const p of PRODUCTS_NICHE) {
- aesthetics[p.aesthetic] = (aesthetics[p.aesthetic] || 0) + 1;
- vendors[p.vendor] = (vendors[p.vendor] || 0) + 1;
- }
- res.json({ aesthetics, vendors, total: PRODUCTS_NICHE.length });
+ const { q, aesthetic } = req.query;
+ const filtered = applyFilters(PRODUCTS_NICHE, { q, aesthetic });
+ const aesthetics = {};
+ for (const p of filtered) aesthetics[p.aesthetic] = (aesthetics[p.aesthetic] || 0) + 1;
+ res.json({ aesthetics, total: filtered.length });
});
app.get('/api/health', (req, res) => res.json({ status: 'ok', count: PRODUCTS_NICHE.length, dropped: DROPPED }));
← de7e50d retag aesthetic from PG: 350 rows updated
·
back to Fabricwallpaper
·
redact vendor names from customer-facing /api (close fleet l 3191a91 →