[object Object]

← back to Wallco Ai

bulk-action 'digital': exempt designs with a built room-size mural master from demotion

5499527ae945cc8a7bd7ca775cca061cfcbdba51 · 2026-06-03 22:34:16 -0700 · Steve Abrams

The low-res mural demotion (mark digital-for-sale → unpublish) was unpublishing
designs that NOW have a built 12x11ft master (e.g. #27151) — i.e. premium mural
products got demoted as 'low-res junk'. Guard: skip any id with a
data/mural-previews/<id>.jpg, report them as mural_skipped. Prevents the
mural-block PDPs from being silently 404'd by the demotion.

Files touched

Diff

commit 5499527ae945cc8a7bd7ca775cca061cfcbdba51
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jun 3 22:34:16 2026 -0700

    bulk-action 'digital': exempt designs with a built room-size mural master from demotion
    
    The low-res mural demotion (mark digital-for-sale → unpublish) was unpublishing
    designs that NOW have a built 12x11ft master (e.g. #27151) — i.e. premium mural
    products got demoted as 'low-res junk'. Guard: skip any id with a
    data/mural-previews/<id>.jpg, report them as mural_skipped. Prevents the
    mural-block PDPs from being silently 404'd by the demotion.
---
 server.js | 52 +++++++++++++++++++++++++++++-----------------------
 1 file changed, 29 insertions(+), 23 deletions(-)

diff --git a/server.js b/server.js
index 33ef3f7..79b8f60 100644
--- a/server.js
+++ b/server.js
@@ -4989,6 +4989,7 @@ app.post('/api/designs/bulk-action', express.json({ limit: '64kb' }), (req, res)
   const idList = ids.join(',');
   try {
     let affected = 0;
+    let muralSkipped = [];
     if (action === 'delete') {
       psqlExecLocal(`UPDATE spoon_all_designs SET user_removed=TRUE WHERE id IN (${idList});`);
       affected = ids.length;
@@ -5004,34 +5005,39 @@ app.post('/api/designs/bulk-action', express.json({ limit: '64kb' }), (req, res)
       psqlExecLocal(`UPDATE spoon_all_designs SET is_published=FALSE WHERE id IN (${idList});`);
       affected = ids.length;
     } else if (action === 'digital') {
-      // Unpublish from the wallpaper catalog AND mark as a digital file for sale.
-      // Mirrors the proven cactus-curator 'digital' verdict (lines ~3018): off
-      // the live catalog (is_published/web_viewer=FALSE), stamp digital_file_at,
-      // and tag 'digital-file' so the digital-pack pipeline picks it up.
-      // Written against the base table all_designs (the columns digital_file_at /
-      // web_viewer / tags live there) — same target the cactus path uses.
-      psqlExecLocal(`UPDATE all_designs
-        SET is_published=FALSE, web_viewer=FALSE, digital_file_at=now(),
-            tags = array_remove(COALESCE(tags, ARRAY[]::text[]), 'digital-file') || ARRAY['digital-file']::text[]
-        WHERE id IN (${idList});`);
-      // PROD spoon_all_designs is a SEPARATE base table the storefront/PDP reads;
-      // on Mac2 it's a VIEW over all_designs so this is a harmless echo. The
-      // 'unpublish' action already writes spoon — 'digital' must too, or on prod
-      // the item stays visible on its PDP after being marked digital-for-sale
-      // (the all_designs-only write doesn't reach the table the live site reads).
-      // This is the root cause of the digital-for-sale leak. Steve 2026-06-03.
-      psqlExecLocal(`UPDATE spoon_all_designs SET is_published=FALSE WHERE id IN (${idList});`);
-      affected = ids.length;
-      // Drop from in-memory catalog so it leaves the live feed immediately.
-      for (const id of ids) {
-        const idx = DESIGNS.findIndex((x) => x.id === id);
-        if (idx >= 0) DESIGNS.splice(idx, 1);
+      // GUARD (Steve 2026-06-04): a design that's been built into a 12x11ft
+      // room-size mural master (data/mural-previews/<id>.jpg) is now a PREMIUM
+      // mural product — never silently demote it to digital-only. The low-res
+      // demotion was unpublishing exactly these (e.g. #27151). Skip + report them.
+      muralSkipped = ids.filter((id) => {
+        try { return fs.existsSync(path.join(__dirname, 'data', 'mural-previews', String(id) + '.jpg')); }
+        catch (e) { return false; }
+      });
+      const toDemote = ids.filter((id) => !muralSkipped.includes(id));
+      if (toDemote.length) {
+        const dList = toDemote.join(',');
+        // Unpublish from the wallpaper catalog AND mark as a digital file for sale
+        // (is_published/web_viewer=FALSE, stamp digital_file_at, tag 'digital-file').
+        psqlExecLocal(`UPDATE all_designs
+          SET is_published=FALSE, web_viewer=FALSE, digital_file_at=now(),
+              tags = array_remove(COALESCE(tags, ARRAY[]::text[]), 'digital-file') || ARRAY['digital-file']::text[]
+          WHERE id IN (${dList});`);
+        // PROD spoon_all_designs is a SEPARATE base table the storefront/PDP reads
+        // (on Mac2 it's a VIEW → harmless echo); mirror the unpublish so digital
+        // actually drops the item from the live site on prod.
+        psqlExecLocal(`UPDATE spoon_all_designs SET is_published=FALSE WHERE id IN (${dList});`);
+        // Drop from in-memory catalog so it leaves the live feed immediately.
+        for (const id of toDemote) {
+          const idx = DESIGNS.findIndex((x) => x.id === id);
+          if (idx >= 0) DESIGNS.splice(idx, 1);
+        }
       }
+      affected = toDemote.length;
     } else if (action === 'tag') {
       // For now — accept but don't implement tag-write path (needs UI for tag input)
       return res.status(501).json({ error: 'tag action not yet implemented — coming soon' });
     }
-    res.json({ ok: true, action, affected, ids });
+    res.json({ ok: true, action, affected, ids, mural_skipped: muralSkipped });
   } catch (e) {
     res.status(500).json({ error: e.message });
   }

← d2aa744 Add clean-edge vector-trace 150-DPI upscale pipeline (potrac  ·  back to Wallco Ai  ·  Feature murals prominently on home: pinned Shop Murals quick c85a001 →