← back to Wallco Ai
BUG-1: rename duplicate /api/design/:id/rate → /score (Express 5 first-wins)
e14743cdd6de649d1a9e71e0203de5c19084f9da · 2026-05-23 11:52:26 -0700 · Steve Abrams
Two handlers were registered at the same path with different semantics:
server.js:1488 — POST /api/design/:id/rate { color_good, style_good,
scale_good, stars } → updates spoon_all_designs.user_*
server.js:6388 — POST /api/design/:id/rate { color, style, composition,
spacing, invert, notes } → INSERTs design_ratings row
Express 5 resolves first-registered-wins, so the handler at 1488 always
fired and the 1-10 expert-rating panel at 6388 was permanently dead code
on prod. The bug was invisible because both gate on isAdmin so the 1488
handler 404s on bad bodies, looking like "not implemented" rather than
"wrong handler took it."
Renamed the second handler to POST /api/design/:id/score and updated the
two frontend callers (the admin review panel at server.js:9713,9719).
The first handler keeps /rate (it's been the live one). The GET
/api/design/:id/ratings (line 9727) reads from design_ratings unchanged.
Files touched
Diff
commit e14743cdd6de649d1a9e71e0203de5c19084f9da
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat May 23 11:52:26 2026 -0700
BUG-1: rename duplicate /api/design/:id/rate → /score (Express 5 first-wins)
Two handlers were registered at the same path with different semantics:
server.js:1488 — POST /api/design/:id/rate { color_good, style_good,
scale_good, stars } → updates spoon_all_designs.user_*
server.js:6388 — POST /api/design/:id/rate { color, style, composition,
spacing, invert, notes } → INSERTs design_ratings row
Express 5 resolves first-registered-wins, so the handler at 1488 always
fired and the 1-10 expert-rating panel at 6388 was permanently dead code
on prod. The bug was invisible because both gate on isAdmin so the 1488
handler 404s on bad bodies, looking like "not implemented" rather than
"wrong handler took it."
Renamed the second handler to POST /api/design/:id/score and updated the
two frontend callers (the admin review panel at server.js:9713,9719).
The first handler keeps /rate (it's been the live one). The GET
/api/design/:id/ratings (line 9727) reads from design_ratings unchanged.
---
server.js | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/server.js b/server.js
index 8ba3a39..f54f911 100644
--- a/server.js
+++ b/server.js
@@ -6384,8 +6384,13 @@ function adminRatingGate(req, res) {
return true;
}
-// POST /api/design/:id/rate { color, style, composition, spacing?, invert?, notes? }
-app.post('/api/design/:id/rate', express.json({ limit: '128kb' }), (req, res) => {
+// POST /api/design/:id/score { color, style, composition, spacing?, invert?, notes? }
+// BUG-1 (2026-05-23): was POST /api/design/:id/rate — collided with the
+// user-feedback toggle endpoint at line 1488. Express 5 first-wins
+// resolution silently shadowed this handler, making the entire 1-10
+// expert-rating panel dead code on prod. Renamed to /score; the frontend
+// admin review panel (server.js:9713,9719) is updated in the same commit.
+app.post('/api/design/:id/score', express.json({ limit: '128kb' }), (req, res) => {
if (!adminRatingGate(req, res)) return;
const id = parseInt(req.params.id, 10);
const { color, style, composition, spacing, invert, notes } = req.body || {};
@@ -9710,13 +9715,13 @@ ${htmlHeader('/designs')}
async function post(url, body){var r=await fetch(url+'?review=1',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});return r.json();}
document.getElementById('btn-rate-save').addEventListener('click', async function(){
setStatus('Saving…');
- var j = await post('/api/design/'+id+'/rate', payload());
+ var j = await post('/api/design/'+id+'/score', payload());
setStatus(j.ok ? 'Saved rating #' + j.rating_id : ('Error: ' + (j.error||'?')), !j.ok);
if (j.ok) loadHistory();
});
document.getElementById('btn-rate-retry').addEventListener('click', async function(){
setStatus('Saving + queueing retry…');
- var j = await post('/api/design/'+id+'/rate', payload());
+ var j = await post('/api/design/'+id+'/score', payload());
if (!j.ok) { setStatus('Error: '+(j.error||'?'), true); return; }
var k = await post('/api/design/'+id+'/retry', {});
setStatus(k.ok ? ('Retry queued — nudges: ' + (k.nudges||[]).join(' · ')) : ('Retry error: '+(k.error||'?')), !k.ok);
← dc21aab SEC-5: drop local_path from public response paths
·
back to Wallco Ai
·
BUG-2: qwen3 timeout 60s → 3s + 5-failure circuit breaker 65f1266 →