← back to Wallpaperdistributors
Replace vendor-leaking distributor directory with thin vendor-free DW trade bridge; archive full build under _legacy-directory/ (DTD verdict B)
ebb076a52fcb3fe533c56c4623b36633b8c0c23e · 2026-05-31 17:04:17 -0700 · steve
Files touched
A DEPLOY.mdR100 data/distributors.json _legacy-directory/data/distributors.jsonA _legacy-directory/server.jsR100 views/404.ejs _legacy-directory/views/404.ejsR100 views/about.ejs _legacy-directory/views/about.ejsR100 views/distributor.ejs _legacy-directory/views/distributor.ejsR100 views/index.ejs _legacy-directory/views/index.ejsR100 views/line.ejs _legacy-directory/views/line.ejsR100 views/partials/foot.ejs _legacy-directory/views/partials/foot.ejsR100 views/partials/head.ejs _legacy-directory/views/partials/head.ejsA public/index.htmlM server.js
Diff
commit ebb076a52fcb3fe533c56c4623b36633b8c0c23e
Author: steve <steve@designerwallcoverings.com>
Date: Sun May 31 17:04:17 2026 -0700
Replace vendor-leaking distributor directory with thin vendor-free DW trade bridge; archive full build under _legacy-directory/ (DTD verdict B)
---
DEPLOY.md | 36 +++++
{data => _legacy-directory/data}/distributors.json | 0
_legacy-directory/server.js | 142 ++++++++++++++++++++
{views => _legacy-directory/views}/404.ejs | 0
{views => _legacy-directory/views}/about.ejs | 0
{views => _legacy-directory/views}/distributor.ejs | 0
{views => _legacy-directory/views}/index.ejs | 0
{views => _legacy-directory/views}/line.ejs | 0
.../views}/partials/foot.ejs | 0
.../views}/partials/head.ejs | 0
public/index.html | 135 +++++++++++++++++++
server.js | 146 ++++-----------------
12 files changed, 335 insertions(+), 124 deletions(-)
diff --git a/DEPLOY.md b/DEPLOY.md
new file mode 100644
index 0000000..52b8363
--- /dev/null
+++ b/DEPLOY.md
@@ -0,0 +1,36 @@
+# wallpaperdistributors.com — deploy runbook
+
+**Status (2026-05-31):** Shipped as a **thin, vendor-free trade bridge** (DTD verdict B +
+Steve's call). The original standalone *distributor directory* build is **archived under
+`_legacy-directory/`** — it was a hard violation of the "never DW vendors in public UI"
+rule (its content was vendor brand names; DW's own record exposed its lines). The
+served app is now a single static DW-branded trade lander → `designerwallcoverings.com/pages/trade`.
+
+- Served homepage: vendor-leak scanned → **0 hits**.
+- Legacy routes `/distributor/:id` and `/line/:line` → **404** (archived, not served).
+
+## Registrar / DNS facts
+- Registrar **GoDaddy** · expires 2027-11-06 · **locked + privacy ON**
+- Nameservers: `ns5.afternic.com` / `ns6.afternic.com` (Afternic parking)
+- Target host: Kamatera `45.61.58.125`, app port **9932**
+
+## STEP 1 — Take it off Afternic *(Steve — account action)*
+GoDaddy → the domain → *List for Sale / Afternic* → remove listing.
+
+## STEP 2 — Repoint DNS *(Steve — registrar action / DNS is classifier-gated)*
+GoDaddy → unlock → GoDaddy default nameservers → **A `@` → 45.61.58.125** (+ `www`) →
+re-lock. Verify `dig +short wallpaperdistributors.com` = `45.61.58.125`.
+
+## STEP 3 — Deploy to Kamatera *(Claude, once DNS resolves)*
+```sh
+# EXCLUDE the legacy vendor-laden directory build from prod — never deploy _legacy-directory/
+rsync -az --exclude node_modules --exclude .git --exclude _legacy-directory \
+ ~/Projects/wallpaperdistributors/ my-server:/root/Projects/wallpaperdistributors/
+ssh my-server 'cd /root/Projects/wallpaperdistributors && npm install --omit=dev'
+ssh my-server 'cd /root/Projects/wallpaperdistributors && pm2 start server.js --name wallpaperdistributors && pm2 save'
+# nginx vhost → 127.0.0.1:9932 ; certbot --nginx -d wallpaperdistributors.com -d www.…
+```
+
+> If a real distributor directory is ever wanted, the legacy build in `_legacy-directory/`
+> must be vendor-scrubbed first (rename/neutralize every `lines_carried` brand) before it
+> can be served — do not deploy it as-is.
diff --git a/data/distributors.json b/_legacy-directory/data/distributors.json
similarity index 100%
rename from data/distributors.json
rename to _legacy-directory/data/distributors.json
diff --git a/_legacy-directory/server.js b/_legacy-directory/server.js
new file mode 100644
index 0000000..6a5c827
--- /dev/null
+++ b/_legacy-directory/server.js
@@ -0,0 +1,142 @@
+const express = require('express');
+const path = require('path');
+const fs = require('fs');
+const helmet = require('helmet');
+const compression = require('compression');
+const morgan = require('morgan');
+
+const app = express();
+const PORT = process.env.PORT || 9932;
+const GA_ID = process.env.GA_ID || 'G-NBZ5WTBW8F';
+
+const distributors = JSON.parse(
+ fs.readFileSync(path.join(__dirname, 'data', 'distributors.json'), 'utf8')
+);
+
+app.set('view engine', 'ejs');
+app.set('views', path.join(__dirname, 'views'));
+
+app.use(helmet({
+ contentSecurityPolicy: false,
+ crossOriginEmbedderPolicy: false,
+}));
+app.use(compression());
+app.use(morgan('tiny'));
+
+app.use((req, res, next) => {
+ res.set('Cache-Control', 'no-store, must-revalidate');
+ next();
+});
+
+// Never serve editor/snapshot artifacts from the static root.
+app.use((req, res, next) => {
+ if (/\.(bak|orig|pre-[^/]*|[^/]*~)$|\.bak\.|\.pre-/i.test(req.path)) {
+ return res.status(404).render('404', { site: SITE });
+ }
+ next();
+});
+
+app.use(express.static(path.join(__dirname, 'public'), { maxAge: '1h' }));
+
+const allRegions = Array.from(new Set(distributors.flatMap(d => d.regions))).sort();
+const allSegments = Array.from(new Set(distributors.flatMap(d => d.segments))).sort();
+const allLines = Array.from(new Set(distributors.flatMap(d => d.lines_carried))).sort();
+
+const SITE = {
+ domain: 'wallpaperdistributors.com',
+ name: 'Wallpaper Distributors',
+ tagline: 'B2B wallcovering distributors, mapped to the lines they carry',
+ description: 'Trade index of B2B wallcovering distributors across the U.S. and Canada — searchable by vendor line, region, and project segment. Find which distributor stocks Schumacher, Thibaut, Phillip Jeffries, Cole & Son, Maya Romanoff, Wolf-Gordon, Kravet, and other lines for residential, hospitality, healthcare, multifamily, and commercial projects.',
+ email: 'info@wallpaperdistributors.com',
+ phone: '888-373-4564',
+ address: 'Designer Wallcoverings, 15442 Ventura Bl #102, Sherman Oaks CA 91403',
+ ga_id: GA_ID,
+};
+
+function sortDistributors(list, sort) {
+ const copy = list.slice();
+ switch (sort) {
+ case 'company':
+ return copy.sort((a, b) => a.company.localeCompare(b.company));
+ case 'city':
+ return copy.sort((a, b) => a.city.localeCompare(b.city));
+ case 'lines':
+ return copy.sort((a, b) => b.lines_carried.length - a.lines_carried.length);
+ case 'years':
+ return copy.sort((a, b) => a.founded - b.founded);
+ case 'newest':
+ default:
+ return copy.sort((a, b) => b.id - a.id);
+ }
+}
+
+app.get('/', (req, res) => {
+ const { q, region, segment, line, trade, sort } = req.query;
+ let filtered = distributors.slice();
+
+ if (q) {
+ const needle = String(q).toLowerCase();
+ filtered = filtered.filter(d =>
+ d.company.toLowerCase().includes(needle) ||
+ d.city.toLowerCase().includes(needle) ||
+ d.lines_carried.some(l => l.toLowerCase().includes(needle)) ||
+ d.segments.some(s => s.toLowerCase().includes(needle)) ||
+ (d.notes || '').toLowerCase().includes(needle)
+ );
+ }
+ if (region) filtered = filtered.filter(d => d.regions.includes(String(region)));
+ if (segment) filtered = filtered.filter(d => d.segments.includes(String(segment)));
+ if (line) filtered = filtered.filter(d => d.lines_carried.includes(String(line)));
+ if (trade === '1') filtered = filtered.filter(d => d.trade_only);
+
+ filtered = sortDistributors(filtered, sort);
+
+ res.render('index', {
+ site: SITE,
+ distributors: filtered,
+ total: distributors.length,
+ matched: filtered.length,
+ filters: {
+ q: q || '',
+ region: region || '',
+ segment: segment || '',
+ line: line || '',
+ trade: trade || '',
+ sort: sort || 'newest',
+ },
+ facets: { regions: allRegions, segments: allSegments, lines: allLines },
+ });
+});
+
+app.get('/distributor/:id', (req, res) => {
+ const id = Number(req.params.id);
+ const distributor = distributors.find(d => d.id === id);
+ if (!distributor) return res.status(404).render('404', { site: SITE });
+ const related = distributors
+ .filter(d => d.id !== distributor.id && d.lines_carried.some(l => distributor.lines_carried.includes(l)))
+ .slice(0, 4);
+ res.render('distributor', { site: SITE, distributor, related });
+});
+
+app.get('/line/:line', (req, res) => {
+ const line = req.params.line;
+ const carriers = distributors.filter(d => d.lines_carried.includes(line));
+ if (!carriers.length) return res.status(404).render('404', { site: SITE });
+ res.render('line', { site: SITE, line, carriers });
+});
+
+app.get('/about', (req, res) => {
+ res.render('about', { site: SITE });
+});
+
+app.get('/healthz', (req, res) =>
+ res.json({ ok: true, port: PORT, distributors: distributors.length })
+);
+
+app.use((req, res) => res.status(404).render('404', { site: SITE }));
+
+app.listen(PORT, () => {
+ console.log(
+ `wallpaperdistributors listening on :${PORT} (${distributors.length} distributors loaded, GA ${GA_ID})`
+ );
+});
diff --git a/views/404.ejs b/_legacy-directory/views/404.ejs
similarity index 100%
rename from views/404.ejs
rename to _legacy-directory/views/404.ejs
diff --git a/views/about.ejs b/_legacy-directory/views/about.ejs
similarity index 100%
rename from views/about.ejs
rename to _legacy-directory/views/about.ejs
diff --git a/views/distributor.ejs b/_legacy-directory/views/distributor.ejs
similarity index 100%
rename from views/distributor.ejs
rename to _legacy-directory/views/distributor.ejs
diff --git a/views/index.ejs b/_legacy-directory/views/index.ejs
similarity index 100%
rename from views/index.ejs
rename to _legacy-directory/views/index.ejs
diff --git a/views/line.ejs b/_legacy-directory/views/line.ejs
similarity index 100%
rename from views/line.ejs
rename to _legacy-directory/views/line.ejs
diff --git a/views/partials/foot.ejs b/_legacy-directory/views/partials/foot.ejs
similarity index 100%
rename from views/partials/foot.ejs
rename to _legacy-directory/views/partials/foot.ejs
diff --git a/views/partials/head.ejs b/_legacy-directory/views/partials/head.ejs
similarity index 100%
rename from views/partials/head.ejs
rename to _legacy-directory/views/partials/head.ejs
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..082a07f
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html lang="en" data-theme="light">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>Wallpaper Distributors — Trade Wallcovering Distribution | Designer Wallcoverings</title>
+<meta name="description" content="Trade wallcovering distribution for designers, hospitality, healthcare, multifamily, and commercial projects — free memos, trade pricing, and nationwide fulfillment through Designer Wallcoverings. Apply for a trade account.">
+<link rel="canonical" href="https://wallpaperdistributors.com/">
+<meta property="og:title" content="Wallpaper Distributors — Trade Wallcovering Distribution">
+<meta property="og:description" content="One trade partner for every luxury wallcovering line. Free memos, trade pricing, nationwide fulfillment. By Designer Wallcoverings.">
+<meta property="og:type" content="website">
+<meta property="og:url" content="https://wallpaperdistributors.com/">
+<style>
+ :root{
+ --bg:#faf7f2; --fg:#1a1712; --muted:#7c736a; --rule:#e2d9cc; --accent:#1f3a2a;
+ --accent-ink:#fff; --card:#ffffff; --soft:#eef2ee;
+ --serif:'Cormorant Garamond','Playfair Display',Georgia,serif;
+ --sans:-apple-system,BlinkMacSystemFont,'Inter',system-ui,sans-serif;
+ }
+ [data-theme=dark]{ --bg:#0e0c09; --fg:#f2ede4; --muted:#9a9088; --rule:#2c2620; --card:#16130f; --accent:#bbe3c5; --accent-ink:#0e0c09; --soft:#1a221b; }
+ *{box-sizing:border-box} html,body{margin:0;padding:0}
+ body{background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.55;-webkit-font-smoothing:antialiased}
+ a{color:var(--accent);text-decoration:none}
+ .wrap{max-width:1000px;margin:0 auto;padding:0 22px}
+ header.bar{display:flex;align-items:center;justify-content:space-between;padding:20px 22px;max-width:1000px;margin:0 auto}
+ .logo{font-family:var(--serif);font-size:22px;font-weight:600;letter-spacing:.03em}
+ .logo small{display:block;font-family:var(--sans);font-size:10px;letter-spacing:.22em;text-transform:uppercase;color:var(--muted);font-weight:600}
+ .theme-btn{background:none;border:1px solid var(--rule);color:var(--fg);border-radius:50%;width:38px;height:38px;cursor:pointer}
+
+ .hero{text-align:center;padding:46px 0 18px}
+ .hero .eyebrow{font-size:12px;letter-spacing:.22em;text-transform:uppercase;color:var(--muted);font-weight:600;margin:0 0 14px}
+ .hero h1{font-family:var(--serif);font-weight:600;font-size:clamp(38px,6.5vw,64px);line-height:1.04;margin:0 0 14px}
+ .hero p{color:var(--muted);font-size:clamp(16px,2.4vw,20px);max-width:660px;margin:0 auto 26px}
+ .cta-row{display:flex;gap:14px;justify-content:center;flex-wrap:wrap}
+ .btn{display:inline-block;font-weight:700;padding:15px 30px;border-radius:9px;font-size:15px}
+ .btn-primary{background:var(--accent);color:var(--accent-ink)}
+ .btn-primary:hover{filter:brightness(1.08)}
+ .btn-ghost{border:1px solid var(--rule);color:var(--fg)}
+ .btn-ghost:hover{background:var(--soft)}
+
+ .strip{background:var(--soft);border-block:1px solid var(--rule);margin:44px 0;padding:30px 0}
+ .strip .grid{display:grid;grid-template-columns:repeat(4,1fr);gap:22px;text-align:center}
+ @media(max-width:720px){.strip .grid{grid-template-columns:repeat(2,1fr);gap:26px}}
+ .strip .grid b{font-family:var(--serif);font-size:30px;color:var(--accent);display:block;line-height:1}
+ .strip .grid span{font-size:13px;color:var(--muted);display:block;margin-top:6px}
+
+ .feat{display:grid;grid-template-columns:repeat(3,1fr);gap:26px;padding:8px 0 18px}
+ @media(max-width:760px){.feat{grid-template-columns:1fr}}
+ .feat .c{background:var(--card);border:1px solid var(--rule);border-radius:12px;padding:24px}
+ .feat .c h3{font-family:var(--serif);font-size:21px;font-weight:600;margin:0 0 8px}
+ .feat .c p{color:var(--muted);font-size:14.5px;margin:0}
+
+ .closer{text-align:center;padding:50px 0 30px}
+ .closer h2{font-family:var(--serif);font-size:clamp(28px,4.5vw,42px);font-weight:600;margin:0 0 12px}
+ .closer p{color:var(--muted);max-width:600px;margin:0 auto 24px}
+
+ footer{border-top:1px solid var(--rule);padding:30px 0;text-align:center;color:var(--muted);font-size:13px;margin-top:30px}
+ footer a{color:var(--accent)}
+ footer address{font-style:normal;margin-top:8px;line-height:1.7}
+</style>
+</head>
+<body>
+<header class="bar">
+ <div class="logo">Wallpaper Distributors<small>Trade Distribution · by Designer Wallcoverings</small></div>
+ <button class="theme-btn" id="themeBtn" aria-label="Toggle dark mode">◐</button>
+</header>
+
+<div class="wrap">
+ <section class="hero">
+ <p class="eyebrow">Trade & Contract Wallcovering Supply</p>
+ <h1>One distribution partner<br>for every luxury line.</h1>
+ <p>Designers, hospitality groups, healthcare and multifamily specifiers source the world's finest wallcoverings through a single trade account — free memos, trade pricing, expert specification help, and nationwide fulfillment.</p>
+ <div class="cta-row">
+ <a class="btn btn-primary" href="https://designerwallcoverings.com/pages/trade" target="_blank" rel="noopener">Apply for a trade account →</a>
+ <a class="btn btn-ghost" href="https://designerwallcoverings.com/collections/all" target="_blank" rel="noopener">Browse the catalog</a>
+ </div>
+ </section>
+</div>
+
+<div class="strip">
+ <div class="wrap">
+ <div class="grid">
+ <div><b>11,000+</b><span>patterns across every premier house</span></div>
+ <div><b>Free</b><span>memos & CFAs to the trade</span></div>
+ <div><b>Nationwide</b><span>residential, hospitality & commercial</span></div>
+ <div><b>3–5 days</b><span>typical memo turnaround</span></div>
+ </div>
+ </div>
+</div>
+
+<div class="wrap">
+ <section class="feat">
+ <div class="c">
+ <h3>Every line, one account</h3>
+ <p>Skip juggling a dozen showroom logins. Specify across the full breadth of luxury wallcovering — grasscloth, silk, hand-painted scenics, performance vinyl, metallics — through one trade relationship.</p>
+ </div>
+ <div class="c">
+ <h3>Trade pricing & terms</h3>
+ <p>Net pricing for qualified designers and contract buyers, project quoting, and FF&E-friendly fulfillment for hospitality, healthcare, and multifamily specifications.</p>
+ </div>
+ <div class="c">
+ <h3>Specification support</h3>
+ <p>Real people who know the products — memo programs, dye-lot management, repeat and railroading guidance, and lead-time confirmation before you commit an order.</p>
+ </div>
+ </section>
+
+ <section class="closer">
+ <h2>Specifying a project?</h2>
+ <p>Apply for a Designer Wallcoverings trade account and get pricing, memos, and a specification contact for your next install.</p>
+ <a class="btn btn-primary" href="https://designerwallcoverings.com/pages/trade" target="_blank" rel="noopener">Apply for trade pricing →</a>
+ </section>
+</div>
+
+<footer>
+ <div class="wrap">
+ <strong>Wallpaper Distributors</strong> — trade wallcovering distribution by
+ <a href="https://designerwallcoverings.com" target="_blank" rel="noopener">Designer Wallcoverings</a>.
+ <address>
+ Designer Wallcoverings · 15442 Ventura Bl #102, Sherman Oaks, CA 91403 ·
+ <a href="tel:8883734564">888-373-4564</a> ·
+ <a href="mailto:info@wallpaperdistributors.com">info@wallpaperdistributors.com</a>
+ </address>
+ <div style="margin-top:12px;font-size:11px">© <span id="yr"></span> Designer Wallcoverings.</div>
+ </div>
+</footer>
+
+<script>
+ var tb=document.getElementById('themeBtn');
+ function setTheme(t){document.documentElement.setAttribute('data-theme',t);try{localStorage.setItem('wd-theme',t);}catch(e){}}
+ try{var s=localStorage.getItem('wd-theme');if(s)setTheme(s);}catch(e){}
+ tb.addEventListener('click',function(){setTheme(document.documentElement.getAttribute('data-theme')==='dark'?'light':'dark');});
+ document.getElementById('yr').textContent=new Date().getFullYear();
+</script>
+</body>
+</html>
diff --git a/server.js b/server.js
index 6a5c827..af82f34 100644
--- a/server.js
+++ b/server.js
@@ -1,142 +1,40 @@
+/**
+ * wallpaperdistributors.com — thin trade bridge.
+ *
+ * DTD verdict B + Steve 2026-05-31: do NOT run the standalone distributor
+ * directory (its content was vendor-brand names = a hard violation of the
+ * "never DW vendors in public UI" rule). Instead this is a thin, vendor-free
+ * DW-branded trade lander that routes qualified buyers into the Designer
+ * Wallcoverings trade program. The legacy directory build is preserved under
+ * _legacy-directory/ (NOT served, NOT deployed).
+ */
const express = require('express');
-const path = require('path');
-const fs = require('fs');
const helmet = require('helmet');
-const compression = require('compression');
-const morgan = require('morgan');
+const path = require('path');
-const app = express();
const PORT = process.env.PORT || 9932;
-const GA_ID = process.env.GA_ID || 'G-NBZ5WTBW8F';
+const SITE = 'wallpaperdistributors';
-const distributors = JSON.parse(
- fs.readFileSync(path.join(__dirname, 'data', 'distributors.json'), 'utf8')
-);
-
-app.set('view engine', 'ejs');
-app.set('views', path.join(__dirname, 'views'));
-
-app.use(helmet({
- contentSecurityPolicy: false,
- crossOriginEmbedderPolicy: false,
-}));
-app.use(compression());
-app.use(morgan('tiny'));
+const app = express();
+app.use(helmet({ contentSecurityPolicy: false }));
app.use((req, res, next) => {
- res.set('Cache-Control', 'no-store, must-revalidate');
+ res.setHeader('Cache-Control', 'no-store, must-revalidate');
next();
});
-// Never serve editor/snapshot artifacts from the static root.
+// 404-guard: never serve snapshot/backup files from the static root.
app.use((req, res, next) => {
- if (/\.(bak|orig|pre-[^/]*|[^/]*~)$|\.bak\.|\.pre-/i.test(req.path)) {
- return res.status(404).render('404', { site: SITE });
+ if (/\.(bak|orig|swp|tmp)$|\.bak\.|\.pre-/i.test(req.path)) {
+ return res.status(404).send('Not found');
}
next();
});
-app.use(express.static(path.join(__dirname, 'public'), { maxAge: '1h' }));
-
-const allRegions = Array.from(new Set(distributors.flatMap(d => d.regions))).sort();
-const allSegments = Array.from(new Set(distributors.flatMap(d => d.segments))).sort();
-const allLines = Array.from(new Set(distributors.flatMap(d => d.lines_carried))).sort();
-
-const SITE = {
- domain: 'wallpaperdistributors.com',
- name: 'Wallpaper Distributors',
- tagline: 'B2B wallcovering distributors, mapped to the lines they carry',
- description: 'Trade index of B2B wallcovering distributors across the U.S. and Canada — searchable by vendor line, region, and project segment. Find which distributor stocks Schumacher, Thibaut, Phillip Jeffries, Cole & Son, Maya Romanoff, Wolf-Gordon, Kravet, and other lines for residential, hospitality, healthcare, multifamily, and commercial projects.',
- email: 'info@wallpaperdistributors.com',
- phone: '888-373-4564',
- address: 'Designer Wallcoverings, 15442 Ventura Bl #102, Sherman Oaks CA 91403',
- ga_id: GA_ID,
-};
-
-function sortDistributors(list, sort) {
- const copy = list.slice();
- switch (sort) {
- case 'company':
- return copy.sort((a, b) => a.company.localeCompare(b.company));
- case 'city':
- return copy.sort((a, b) => a.city.localeCompare(b.city));
- case 'lines':
- return copy.sort((a, b) => b.lines_carried.length - a.lines_carried.length);
- case 'years':
- return copy.sort((a, b) => a.founded - b.founded);
- case 'newest':
- default:
- return copy.sort((a, b) => b.id - a.id);
- }
-}
-
-app.get('/', (req, res) => {
- const { q, region, segment, line, trade, sort } = req.query;
- let filtered = distributors.slice();
-
- if (q) {
- const needle = String(q).toLowerCase();
- filtered = filtered.filter(d =>
- d.company.toLowerCase().includes(needle) ||
- d.city.toLowerCase().includes(needle) ||
- d.lines_carried.some(l => l.toLowerCase().includes(needle)) ||
- d.segments.some(s => s.toLowerCase().includes(needle)) ||
- (d.notes || '').toLowerCase().includes(needle)
- );
- }
- if (region) filtered = filtered.filter(d => d.regions.includes(String(region)));
- if (segment) filtered = filtered.filter(d => d.segments.includes(String(segment)));
- if (line) filtered = filtered.filter(d => d.lines_carried.includes(String(line)));
- if (trade === '1') filtered = filtered.filter(d => d.trade_only);
-
- filtered = sortDistributors(filtered, sort);
-
- res.render('index', {
- site: SITE,
- distributors: filtered,
- total: distributors.length,
- matched: filtered.length,
- filters: {
- q: q || '',
- region: region || '',
- segment: segment || '',
- line: line || '',
- trade: trade || '',
- sort: sort || 'newest',
- },
- facets: { regions: allRegions, segments: allSegments, lines: allLines },
- });
-});
-
-app.get('/distributor/:id', (req, res) => {
- const id = Number(req.params.id);
- const distributor = distributors.find(d => d.id === id);
- if (!distributor) return res.status(404).render('404', { site: SITE });
- const related = distributors
- .filter(d => d.id !== distributor.id && d.lines_carried.some(l => distributor.lines_carried.includes(l)))
- .slice(0, 4);
- res.render('distributor', { site: SITE, distributor, related });
-});
-
-app.get('/line/:line', (req, res) => {
- const line = req.params.line;
- const carriers = distributors.filter(d => d.lines_carried.includes(line));
- if (!carriers.length) return res.status(404).render('404', { site: SITE });
- res.render('line', { site: SITE, line, carriers });
-});
-
-app.get('/about', (req, res) => {
- res.render('about', { site: SITE });
-});
-
-app.get('/healthz', (req, res) =>
- res.json({ ok: true, port: PORT, distributors: distributors.length })
-);
+app.use(express.static(path.join(__dirname, 'public'), { extensions: ['html'] }));
-app.use((req, res) => res.status(404).render('404', { site: SITE }));
+app.get('/healthz', (_req, res) => res.json({ ok: true, site: SITE, port: PORT }));
-app.listen(PORT, () => {
- console.log(
- `wallpaperdistributors listening on :${PORT} (${distributors.length} distributors loaded, GA ${GA_ID})`
- );
+app.listen(PORT, '0.0.0.0', () => {
+ console.log(`[${SITE}] thin trade bridge listening on :${PORT}`);
});
← ae37058 Add per-site favicon (kills /favicon.ico 404)
·
back to Wallpaperdistributors
·
fleet rebuild (header-only): dw-header (hamburgers UL + cent cf5cef7 →