build: pre-deploy HTML inline-script syntax guard
A missing brace in a home.html inline <script> caused an app-wide login outage (the whole block failed to parse, so boot never ran). Nothing in the deploy path checked HTML syntax. Add tools/check-syntax.js: parses (never runs) every inline <script> in server/public/*.html with vm and exits non-zero on any syntax error, reporting the file + line. Wired in two places: - Dockerfile RUN after COPY server/ ./ — a syntax error now FAILS the build, so it can't deploy. - redeploy.sh pre-flight — fast local feedback before triggering the remote build. Verified: passes on current pages (16 blocks / 9 files); fails with the exact location when a brace is removed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
c9bb9c75d1
commit
a1f7eef28a
4 files changed
+78
-1
No files matched your search
@@ -16,6 +16,11 @@ RUN npm install --omit=dev --no-audit --no-fund
|
||||
# App source
|
||||
COPY server/ ./
|
||||
|
||||
# Pre-deploy guard: FAIL the build if any inline <script> in the served HTML pages has a JS syntax error. A
|
||||
# broken inline script takes down the whole page (one missing brace in home.html once caused an app-wide login
|
||||
# outage). This turns that into a failed build instead of a production incident. See tools/check-syntax.js.
|
||||
RUN node tools/check-syntax.js
|
||||
|
||||
# Served HTTP port (NPM terminates TLS and proxies here). The DB is Postgres (DATABASE_URL, set via .env);
|
||||
# the /data volume holds uploads / recordings / transcripts / downloads (see docker-compose.yml).
|
||||
ENV PORT=8090
|
||||
|
||||
+6
-1
@@ -1,6 +1,6 @@
|
||||
# Tech Stack — BizGaze Connect
|
||||
|
||||
> Living document. **Last updated: 2026-11-03** (rev 4). Update this file whenever a dependency, platform, service, or
|
||||
> Living document. **Last updated: 2026-11-03** (rev 5). Update this file whenever a dependency, platform, service, or
|
||||
> tool is added/removed/version-bumped. See [Maintaining this file](#maintaining-this-file) and the
|
||||
> [Changelog](#changelog) at the bottom.
|
||||
|
||||
@@ -203,6 +203,11 @@ Bump the **Last updated** date at the top and add a line to the Changelog below.
|
||||
|
||||
## Changelog
|
||||
|
||||
- **2026-11-03 (rev 5)** — Reliability: Postgres pool safety timeouts (`connectionTimeoutMillis`,
|
||||
`statement_timeout`, `idle_in_transaction_session_timeout`, max 10→20) so a saturated pool fails fast instead
|
||||
of hanging forever; and a **pre-deploy HTML syntax guard** (`server/tools/check-syntax.js`) run in the Docker
|
||||
build and `redeploy.sh` — a JS syntax error in an inline `<script>` now fails the build instead of reaching
|
||||
production (it had caused an app-wide login hang).
|
||||
- **2026-11-03 (rev 4)** — Android native call **video** (Increment 2b): `CallVideo` renders camera/screen tiles
|
||||
with LiveKit `TextureViewRenderer` hole-punched behind a transparent WebView; `CallMedia` gains camera
|
||||
publish/flip + track lookup; new `ANDROID_NATIVE_VIDEO` server flag. Also fixed the white meeting background
|
||||
|
||||
@@ -39,5 +39,14 @@ if [ -z "$PW" ]; then
|
||||
read -rsp "Server password for $USER@$HOST: " PW; echo
|
||||
fi
|
||||
|
||||
# Pre-flight: catch inline-<script> syntax errors in the served HTML BEFORE triggering a remote build (fast
|
||||
# local feedback — the Docker build runs the same check authoritatively and would fail anyway). A syntax error
|
||||
# in a big inline script breaks the whole page (it once caused an app-wide login outage). Skipped if node isn't
|
||||
# on PATH. Checks the local tree, which matches origin/master once you've pushed.
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
echo "==> Checking HTML inline-script syntax …"
|
||||
node "$DIR/server/tools/check-syntax.js" || { echo "ERROR: HTML syntax check failed — fix before deploying (the build would fail too)."; exit 1; }
|
||||
fi
|
||||
|
||||
echo "==> Triggering deploy on $USER@$HOST ($APPDIR) …"
|
||||
exec "$PLINK" -ssh -batch -hostkey "$HOSTKEY" -P "$PORT" -pw "$PW" "$USER@$HOST" "cd $APPDIR && bash deploy.sh $*"
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env node
|
||||
// Pre-deploy syntax guard for the served HTML pages.
|
||||
//
|
||||
// WHY: our pages carry large INLINE <script> blocks. A single JavaScript syntax error in one block makes the
|
||||
// WHOLE block fail to parse — every function/handler it defines becomes undefined. That is exactly what caused
|
||||
// an app-wide login outage (home.html's `callConnected` listener was missing one `}`: the boot code never ran,
|
||||
// so after login the splash spun forever). The browser catches it instantly; our deploy did not. This script
|
||||
// turns that class of bug into a FAILED BUILD (exit 1) instead of a production outage.
|
||||
//
|
||||
// It parses — but never executes — every inline <script> in server/public/*.html with Node's `vm`, so it only
|
||||
// checks SYNTAX (browser globals like `document` are irrelevant to parsing). Runs in the Docker build (see
|
||||
// Dockerfile, right after `COPY server/ ./`) and locally from redeploy.sh for fast feedback.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const vm = require('vm');
|
||||
|
||||
const dir = path.join(__dirname, '..', 'public');
|
||||
let files;
|
||||
try {
|
||||
files = fs.readdirSync(dir).filter((f) => f.toLowerCase().endsWith('.html'));
|
||||
} catch (e) {
|
||||
console.error('[check-syntax] cannot read ' + dir + ': ' + e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Inline <script> … </script> (skip ones with a src= attribute — those are external files, nothing inline).
|
||||
const re = /<script\b([^>]*)>([\s\S]*?)<\/script\s*>/gi;
|
||||
let errors = 0;
|
||||
let blocks = 0;
|
||||
|
||||
for (const f of files) {
|
||||
const html = fs.readFileSync(path.join(dir, f), 'utf8');
|
||||
re.lastIndex = 0;
|
||||
let m;
|
||||
while ((m = re.exec(html))) {
|
||||
if (/\bsrc\s*=/i.test(m[1] || '')) continue; // external script
|
||||
blocks++;
|
||||
const code = m[2];
|
||||
// File line where this block's body starts, so the reported line is file-relative (not block-relative).
|
||||
const openTagEnd = m.index + m[0].indexOf('>') + 1;
|
||||
const startLine = html.slice(0, openTagEnd).split('\n').length - 1; // 0-indexed offset
|
||||
try {
|
||||
new vm.Script(code, { filename: f, lineOffset: startLine });
|
||||
} catch (e) {
|
||||
errors++;
|
||||
const loc = (String(e.stack || '').split('\n')[0] || '').trim(); // e.g. "home.html:4398"
|
||||
console.error('[check-syntax] ' + f + ' — ' + (e.message || 'syntax error') +
|
||||
(loc && /:\d+$/.test(loc) ? (' @ ' + loc) : (' (inline <script> near line ' + (startLine + 1) + ')')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors) {
|
||||
console.error('[check-syntax] FAILED: ' + errors + ' inline <script> block(s) with syntax errors. Aborting deploy.');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('[check-syntax] OK: ' + blocks + ' inline <script> block(s) parsed across ' + files.length + ' HTML file(s).');
|
||||
process.exit(0);
|
||||
Reference in new issue
Block a user