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
|
# App source
|
||||||
COPY server/ ./
|
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);
|
# 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).
|
# the /data volume holds uploads / recordings / transcripts / downloads (see docker-compose.yml).
|
||||||
ENV PORT=8090
|
ENV PORT=8090
|
||||||
|
|||||||
+6
-1
@@ -1,6 +1,6 @@
|
|||||||
# Tech Stack — BizGaze Connect
|
# 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
|
> tool is added/removed/version-bumped. See [Maintaining this file](#maintaining-this-file) and the
|
||||||
> [Changelog](#changelog) at the bottom.
|
> [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
|
## 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
|
- **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
|
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
|
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
|
read -rsp "Server password for $USER@$HOST: " PW; echo
|
||||||
fi
|
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) …"
|
echo "==> Triggering deploy on $USER@$HOST ($APPDIR) …"
|
||||||
exec "$PLINK" -ssh -batch -hostkey "$HOSTKEY" -P "$PORT" -pw "$PW" "$USER@$HOST" "cd $APPDIR && bash deploy.sh $*"
|
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