← back to Wallco Ai
tick 14: cool/pale palette batch + snapshot refresh — catalog 77→107 designs · marine 6→15, rose 3→10, neutral 10→13 (the 'cool/oyster/ivory/marine' prompts worked). Reload was hitting wrong pm2 name (wallco-ai-viewer vs actual wallco-ai); confirmed fix.
4541eb0251deafd8e65bed15317b586d1194af97 · 2026-05-11 22:16:40 -0700 · Steve
Files touched
M data/designs.jsonA src/admin-gate.js
Diff
commit 4541eb0251deafd8e65bed15317b586d1194af97
Author: Steve <steve@designerwallcoverings.com>
Date: Mon May 11 22:16:40 2026 -0700
tick 14: cool/pale palette batch + snapshot refresh — catalog 77→107 designs · marine 6→15, rose 3→10, neutral 10→13 (the 'cool/oyster/ivory/marine' prompts worked). Reload was hitting wrong pm2 name (wallco-ai-viewer vs actual wallco-ai); confirmed fix.
---
data/designs.json | 395 +++++++++++++++++++++++++++++++++++++++++++++++++-----
src/admin-gate.js | 47 +++++++
2 files changed, 405 insertions(+), 37 deletions(-)
diff --git a/data/designs.json b/data/designs.json
index ed2b36f..88ff3cf 100644
--- a/data/designs.json
+++ b/data/designs.json
@@ -741,7 +741,14 @@
"seed": 1698245296,
"is_published": false,
"created_at": "2026-05-12T01:01:45.757758+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "rose",
+ "leaf",
+ "vine",
+ "botanical"
+ ]
},
{
"id": 45,
@@ -757,7 +764,13 @@
"seed": 224020684,
"is_published": false,
"created_at": "2026-05-12T01:13:12.328157+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "rose",
+ "leaf",
+ "vine"
+ ]
},
{
"id": 46,
@@ -839,7 +852,13 @@
"seed": 1181433269,
"is_published": false,
"created_at": "2026-05-12T01:44:45.205345+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "rose",
+ "leaf",
+ "vine"
+ ]
},
{
"id": 50,
@@ -1267,7 +1286,13 @@
"seed": 1405845871,
"is_published": false,
"created_at": "2026-05-12T03:03:37.280102+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "butterfly",
+ "trellis",
+ "lattice",
+ "botanical"
+ ]
},
{
"id": 70,
@@ -1437,7 +1462,14 @@
"seed": 432783760,
"is_published": false,
"created_at": "2026-05-12T04:19:14.774939+00:00",
- "motifs": []
+ "motifs": [
+ "geometric",
+ "scrollwork",
+ "damask",
+ "art-nouveau",
+ "abstract",
+ "organic"
+ ]
},
{
"id": 138,
@@ -1453,7 +1485,13 @@
"seed": 1738674239,
"is_published": false,
"created_at": "2026-05-12T04:45:45.035538+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "rose",
+ "leaf",
+ "vine"
+ ]
},
{
"id": 139,
@@ -1469,7 +1507,13 @@
"seed": 1690989889,
"is_published": false,
"created_at": "2026-05-12T04:45:48.971267+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "rose",
+ "leaf",
+ "vine"
+ ]
},
{
"id": 140,
@@ -1485,7 +1529,13 @@
"seed": 1004000158,
"is_published": false,
"created_at": "2026-05-12T04:45:54.657213+00:00",
- "motifs": []
+ "motifs": [
+ "art-deco",
+ "baroque",
+ "damask",
+ "floral",
+ "lattice"
+ ]
},
{
"id": 141,
@@ -1501,7 +1551,14 @@
"seed": 1069042526,
"is_published": false,
"created_at": "2026-05-12T04:45:57.75979+00:00",
- "motifs": []
+ "motifs": [
+ "art-nouveau",
+ "chinoiserie",
+ "toile",
+ "victorian",
+ "floral",
+ "peony"
+ ]
},
{
"id": 142,
@@ -1533,7 +1590,13 @@
"seed": 779545247,
"is_published": false,
"created_at": "2026-05-12T04:46:07.953503+00:00",
- "motifs": []
+ "motifs": [
+ "geometric",
+ "abstract",
+ "organic",
+ "damask",
+ "art-nouveau"
+ ]
},
{
"id": 144,
@@ -1549,7 +1612,13 @@
"seed": 699246130,
"is_published": false,
"created_at": "2026-05-12T04:46:14.659628+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "damask",
+ "scrollwork",
+ "art-nouveau",
+ "baroque"
+ ]
},
{
"id": 145,
@@ -1565,7 +1634,13 @@
"seed": 1387374954,
"is_published": false,
"created_at": "2026-05-12T04:46:17.742426+00:00",
- "motifs": []
+ "motifs": [
+ "geometric",
+ "lattice",
+ "trellis",
+ "damask",
+ "abstract"
+ ]
},
{
"id": 146,
@@ -1581,7 +1656,13 @@
"seed": 817925850,
"is_published": false,
"created_at": "2026-05-12T04:46:24.489402+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "leaf",
+ "botanical",
+ "tropical"
+ ]
},
{
"id": 147,
@@ -1597,7 +1678,12 @@
"seed": 1386604038,
"is_published": false,
"created_at": "2026-05-12T04:46:30.846707+00:00",
- "motifs": []
+ "motifs": [
+ "geometric",
+ "abstract",
+ "organic",
+ "modern"
+ ]
},
{
"id": 148,
@@ -1613,7 +1699,14 @@
"seed": 7344900,
"is_published": false,
"created_at": "2026-05-12T04:49:23.152551+00:00",
- "motifs": []
+ "motifs": [
+ "abstract",
+ "organic",
+ "geometric",
+ "damask",
+ "paisley",
+ "chinoiserie"
+ ]
},
{
"id": 149,
@@ -1629,7 +1722,13 @@
"seed": 901578770,
"is_published": false,
"created_at": "2026-05-12T04:57:52.778618+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "botanical",
+ "leaf",
+ "vine",
+ "damask"
+ ]
},
{
"id": 150,
@@ -1645,7 +1744,13 @@
"seed": 344222204,
"is_published": false,
"created_at": "2026-05-12T05:00:33.919383+00:00",
- "motifs": []
+ "motifs": [
+ "geometric",
+ "lattice",
+ "trellis",
+ "abstract",
+ "organic"
+ ]
},
{
"id": 151,
@@ -1661,7 +1766,13 @@
"seed": 740942429,
"is_published": false,
"created_at": "2026-05-12T05:03:03.419591+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "rose",
+ "leaf",
+ "vine"
+ ]
},
{
"id": 152,
@@ -1677,7 +1788,13 @@
"seed": 2115032576,
"is_published": false,
"created_at": "2026-05-12T05:08:10.167827+00:00",
- "motifs": []
+ "motifs": [
+ "geometric",
+ "scrollwork",
+ "damask",
+ "abstract",
+ "organic"
+ ]
},
{
"id": 153,
@@ -1693,7 +1810,13 @@
"seed": 482166153,
"is_published": false,
"created_at": "2026-05-12T05:11:26.632248+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "rose",
+ "leaf",
+ "vine"
+ ]
},
{
"id": 154,
@@ -1725,7 +1848,13 @@
"seed": 0,
"is_published": false,
"created_at": "2026-05-12T05:11:36.46852+00:00",
- "motifs": []
+ "motifs": [
+ "geometric",
+ "abstract",
+ "organic",
+ "modern",
+ "minimal"
+ ]
},
{
"id": 156,
@@ -1741,7 +1870,13 @@
"seed": 1194092029,
"is_published": false,
"created_at": "2026-05-12T05:11:36.898959+00:00",
- "motifs": []
+ "motifs": [
+ "baroque",
+ "scrollwork",
+ "damask",
+ "abstract",
+ "minimal"
+ ]
},
{
"id": 157,
@@ -1757,7 +1892,13 @@
"seed": 0,
"is_published": false,
"created_at": "2026-05-12T05:11:39.229259+00:00",
- "motifs": []
+ "motifs": [
+ "abstract",
+ "geometric",
+ "modern",
+ "minimal",
+ "dense"
+ ]
},
{
"id": 158,
@@ -1773,7 +1914,14 @@
"seed": 0,
"is_published": false,
"created_at": "2026-05-12T05:11:41.896061+00:00",
- "motifs": []
+ "motifs": [
+ "geometric",
+ "stripe",
+ "dot",
+ "diamond",
+ "hexagon",
+ "abstract"
+ ]
},
{
"id": 159,
@@ -1789,7 +1937,13 @@
"seed": 1951555011,
"is_published": false,
"created_at": "2026-05-12T05:11:46.268715+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "rose",
+ "leaf",
+ "vine"
+ ]
},
{
"id": 160,
@@ -1805,7 +1959,14 @@
"seed": 9047248,
"is_published": false,
"created_at": "2026-05-12T05:11:52.769068+00:00",
- "motifs": []
+ "motifs": [
+ "abstract",
+ "organic",
+ "geometric",
+ "damask",
+ "paisley",
+ "art-deco"
+ ]
},
{
"id": 161,
@@ -1821,7 +1982,13 @@
"seed": 61478237,
"is_published": false,
"created_at": "2026-05-12T05:11:59.332568+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "rose",
+ "leaf",
+ "vine"
+ ]
},
{
"id": 162,
@@ -1837,7 +2004,14 @@
"seed": 8925731,
"is_published": false,
"created_at": "2026-05-12T05:12:02.687675+00:00",
- "motifs": []
+ "motifs": [
+ "abstract",
+ "organic",
+ "wave",
+ "ripple",
+ "ocean",
+ "teal"
+ ]
},
{
"id": 163,
@@ -1853,7 +2027,14 @@
"seed": 2073479218,
"is_published": false,
"created_at": "2026-05-12T05:12:09.246041+00:00",
- "motifs": []
+ "motifs": [
+ "geometric",
+ "stripe",
+ "dot",
+ "diamond",
+ "hexagon",
+ "fan"
+ ]
},
{
"id": 164,
@@ -1869,7 +2050,13 @@
"seed": 1574193382,
"is_published": false,
"created_at": "2026-05-12T05:12:16.013082+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "rose",
+ "leaf",
+ "vine"
+ ]
},
{
"id": 165,
@@ -1885,7 +2072,13 @@
"seed": 1814184402,
"is_published": false,
"created_at": "2026-05-12T05:12:18.597377+00:00",
- "motifs": []
+ "motifs": [
+ "abstract",
+ "geometric",
+ "stripe",
+ "dot",
+ "diamond"
+ ]
},
{
"id": 166,
@@ -1901,7 +2094,13 @@
"seed": 1694820816,
"is_published": false,
"created_at": "2026-05-12T05:12:23.969101+00:00",
- "motifs": []
+ "motifs": [
+ "abstract",
+ "organic",
+ "geometric",
+ "damask",
+ "art-nouveau"
+ ]
},
{
"id": 167,
@@ -1917,7 +2116,12 @@
"seed": 0,
"is_published": false,
"created_at": "2026-05-12T05:12:26.365089+00:00",
- "motifs": []
+ "motifs": [
+ "abstract",
+ "organic",
+ "geometric",
+ "dense"
+ ]
},
{
"id": 168,
@@ -1933,7 +2137,13 @@
"seed": 0,
"is_published": false,
"created_at": "2026-05-12T05:12:28.672177+00:00",
- "motifs": []
+ "motifs": [
+ "abstract",
+ "organic",
+ "geometric",
+ "modern",
+ "minimal"
+ ]
},
{
"id": 169,
@@ -1949,7 +2159,13 @@
"seed": 2004602550,
"is_published": false,
"created_at": "2026-05-12T05:12:29.7754+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "rose",
+ "leaf",
+ "vine"
+ ]
},
{
"id": 170,
@@ -1965,7 +2181,14 @@
"seed": 0,
"is_published": false,
"created_at": "2026-05-12T05:12:31.046533+00:00",
- "motifs": []
+ "motifs": [
+ "abstract",
+ "organic",
+ "art-deco",
+ "art-nouveau",
+ "minimal",
+ "dense"
+ ]
},
{
"id": 171,
@@ -1981,7 +2204,13 @@
"seed": 0,
"is_published": false,
"created_at": "2026-05-12T05:12:32.21682+00:00",
- "motifs": []
+ "motifs": [
+ "floral",
+ "peony",
+ "leaf",
+ "vine",
+ "botanical"
+ ]
},
{
"id": 172,
@@ -1997,6 +2226,98 @@
"seed": 0,
"is_published": false,
"created_at": "2026-05-12T05:12:33.420027+00:00",
+ "motifs": [
+ "abstract",
+ "organic",
+ "geometric",
+ "damask",
+ "art-nouveau"
+ ]
+ },
+ {
+ "id": 173,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#132c2f",
+ "saturation": 0.424,
+ "title": "Marine Atelier No.173",
+ "handle": "wallco-0173",
+ "image_url": "/designs/img/var_169_hue+120_1778562752220.png",
+ "filename": "var_169_hue+120_1778562752220.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-12T05:12:34.544077+00:00",
+ "motifs": [
+ "floral",
+ "peony",
+ "leaf",
+ "vine",
+ "botanical"
+ ]
+ },
+ {
+ "id": 174,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#14100f",
+ "saturation": 0.143,
+ "title": "Rose Salon No.174",
+ "handle": "wallco-0174",
+ "image_url": "/designs/img/var_166_mono_1778562753424.png",
+ "filename": "var_166_mono_1778562753424.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-12T05:12:35.774293+00:00",
+ "motifs": []
+ },
+ {
+ "id": 175,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#132c2f",
+ "saturation": 0.424,
+ "title": "Marine Baroque No.175",
+ "handle": "wallco-0175",
+ "image_url": "/designs/img/var_169_hue+180_1778562754548.png",
+ "filename": "var_169_hue+180_1778562754548.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-12T05:12:37.764494+00:00",
+ "motifs": []
+ },
+ {
+ "id": 176,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#132c2f",
+ "saturation": 0.424,
+ "title": "Marine Damask No.176",
+ "handle": "wallco-0176",
+ "image_url": "/designs/img/var_169_hue-60_1778562757768.png",
+ "filename": "var_169_hue-60_1778562757768.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-12T05:12:40.818116+00:00",
+ "motifs": []
+ },
+ {
+ "id": 177,
+ "kind": "seamless_tile",
+ "category": "damask",
+ "dominant_hex": "#132c2f",
+ "saturation": 0.424,
+ "title": "Marine Heirloom No.177",
+ "handle": "wallco-0177",
+ "image_url": "/designs/img/var_169_mono_1778562760822.png",
+ "filename": "var_169_mono_1778562760822.png",
+ "generator": "wallco.ai",
+ "seed": 0,
+ "is_published": false,
+ "created_at": "2026-05-12T05:12:43.207796+00:00",
"motifs": []
}
]
\ No newline at end of file
diff --git a/src/admin-gate.js b/src/admin-gate.js
new file mode 100644
index 0000000..c2e4924
--- /dev/null
+++ b/src/admin-gate.js
@@ -0,0 +1,47 @@
+// Shared admin gate. Replaces:
+// 1. The trivial `req.query.review === '1'` bypass (anyone on public wallco.ai
+// could append ?review=1 to become admin) — REMOVED entirely.
+// 2. The unverified-signature JWT in server.js:278 (base64-decode only) —
+// replaced with HS256 signature verification via jsonwebtoken.
+//
+// Two access tiers (admin-or-localhost):
+// - Loopback (127.0.0.1, localhost, ::1) — full admin, no cookie needed
+// - Any other hostname (wallco.ai, etc.) — requires valid signed JWT in
+// `dw_auth` cookie or `x-dw-auth` header, role === 'admin', not expired.
+//
+// AUTH_DEV_SECRET env required in production — refuse to grant non-local admin
+// if env unset (per security audit finding #15).
+
+'use strict';
+const jwt = require('jsonwebtoken');
+
+const LOCAL_HOSTS = new Set(['127.0.0.1', 'localhost', '::1']);
+
+function isLocalhost(req) {
+ return LOCAL_HOSTS.has(req.hostname);
+}
+
+function verifyAdminCookie(req) {
+ const token = req.cookies?.dw_auth || req.headers['x-dw-auth'] || '';
+ if (!token) return false;
+ const secret = process.env.AUTH_DEV_SECRET;
+ if (!secret) return false; // refuse to verify with no env-configured secret
+ try {
+ const decoded = jwt.verify(token, secret, { algorithms: ['HS256'] });
+ return decoded?.role === 'admin';
+ } catch {
+ return false; // expired, bad signature, malformed
+ }
+}
+
+function isAdmin(req) {
+ return isLocalhost(req) || verifyAdminCookie(req);
+}
+
+// Express middleware: send 404 (NOT 401 — don't reveal endpoint exists)
+function requireAdmin(req, res, next) {
+ if (!isAdmin(req)) return res.status(404).type('html').send('<h1>404</h1>');
+ next();
+}
+
+module.exports = { isAdmin, requireAdmin, isLocalhost };
← a8488e5 fix(wallco-ai/404): drop is_published filter so recovery row
·
back to Wallco Ai
·
tick 14 (late-fire): room-mockup mvp — generate_room_mockups e1a4e64 →