← back to Dw Marketing Reels
Build-gate for read-only public deploy + nightly push to Kamatera
a1ba7d4edafa5b5f331b4bb8fe9254238b8df844 · 2026-07-12 10:31:09 -0700 · Steve Abrams
Files touched
M public/index.htmlM scripts/cron-run.shM server.js
Diff
commit a1ba7d4edafa5b5f331b4bb8fe9254238b8df844
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Jul 12 10:31:09 2026 -0700
Build-gate for read-only public deploy + nightly push to Kamatera
---
public/index.html | 12 ++++++++++++
scripts/cron-run.sh | 12 +++++++++++-
server.js | 7 +++++++
3 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/public/index.html b/public/index.html
index c5733f7..c7bd454 100644
--- a/public/index.html
+++ b/public/index.html
@@ -138,6 +138,18 @@ async function job(path, label) {
$('#btnRefresh').addEventListener('click', ()=>job('api/refresh','↻ refreshing feed…'));
$('#btnBuild').addEventListener('click', ()=>job('api/build','▶ generating reel (≈30s)…'));
+// read-only public deploy hides the generation controls
+(async () => {
+ try {
+ const cfg = await (await fetch('api/config')).json();
+ if (!cfg.allowBuild) {
+ $('#btnBuild').style.display = 'none';
+ $('#btnRefresh').style.display = 'none';
+ $('#status').textContent = 'Reels are rendered on the studio host and published here automatically.';
+ }
+ } catch {}
+})();
+
loadReels(); loadNA();
</script>
</body>
diff --git a/scripts/cron-run.sh b/scripts/cron-run.sh
index 5b77b0a..f7868e3 100755
--- a/scripts/cron-run.sh
+++ b/scripts/cron-run.sh
@@ -1,5 +1,15 @@
#!/bin/zsh
+# Nightly: refresh the New Arrivals feed, render a fresh reel locally ($0), then
+# push the rendered reels + data up to the Kamatera console so the live gallery updates.
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
export HYPERFRAMES_SKIP_SKILLS=1
cd "$(dirname "$0")/.."
-node scripts/fetch-new-arrivals.mjs && node scripts/build-reel.mjs
+node scripts/fetch-new-arrivals.mjs && node scripts/build-reel.mjs || exit 1
+
+PUSH_HOST="${PUSH_HOST:-root@45.61.58.125}"
+PUSH_PATH="${PUSH_PATH:-/root/public-projects/dw-marketing-reels}"
+if ssh -o BatchMode=yes -o ConnectTimeout=6 "$PUSH_HOST" "test -d $PUSH_PATH" 2>/dev/null; then
+ rsync -az reels/ "$PUSH_HOST:$PUSH_PATH/reels/"
+ rsync -az data/ "$PUSH_HOST:$PUSH_PATH/data/"
+ echo "pushed reels+data -> $PUSH_HOST:$PUSH_PATH"
+fi
diff --git a/server.js b/server.js
index c58c701..e41ab2e 100644
--- a/server.js
+++ b/server.js
@@ -14,6 +14,9 @@ const ROOT = dirname(fileURLToPath(import.meta.url));
const PORT = Number(process.env.PORT || 9846);
const USER = process.env.MKT_USER || 'admin';
const PASS = process.env.MKT_PASS || 'DW2024!';
+// Generation needs the local render toolchain (Chrome/ffmpeg). On the public
+// Kamatera deploy set ALLOW_BUILD=0 — it serves reels only; Mac3 renders + pushes.
+const ALLOW_BUILD = process.env.ALLOW_BUILD !== '0';
const MIME = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript', '.css': 'text/css',
'.json': 'application/json', '.mp4': 'video/mp4', '.png': 'image/png', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml' };
@@ -28,6 +31,8 @@ function authed(req) {
}
function run(script, res) {
+ if (!ALLOW_BUILD) { res.writeHead(400, { 'content-type': 'application/json' })
+ .end(JSON.stringify({ error: 'Generation runs on the render host (Mac3). This deploy serves reels only.' })); return; }
if (building) { res.writeHead(409).end(JSON.stringify({ error: 'a job is already running' })); return; }
building = true; lastLog = '';
const env = { ...process.env, PATH: `/opt/homebrew/bin:/usr/local/bin:${process.env.PATH || ''}`, HYPERFRAMES_SKIP_SKILLS: '1' };
@@ -71,6 +76,8 @@ const server = http.createServer(async (req, res) => {
const fp = join(ROOT, 'data', 'new-arrivals.json');
return res.writeHead(200, { 'content-type': 'application/json' }).end(existsSync(fp) ? await readFile(fp) : '{"items":[]}');
}
+ if (p === '/api/config') return res.writeHead(200, { 'content-type': 'application/json' })
+ .end(JSON.stringify({ allowBuild: ALLOW_BUILD }));
if (p === '/api/status') return res.writeHead(200, { 'content-type': 'application/json' })
.end(JSON.stringify({ building, log: lastLog.slice(-4000) }));
if (p === '/api/refresh' && req.method === 'POST') return run('fetch-new-arrivals.mjs', res);
← 7630cbd Add nightly runner (gated plist), README, deploy config
·
back to Dw Marketing Reels
·
Auto-post to IG/TikTok (fail-safe pending-creds) + captions 20c8b48 →