mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
fix(brief): Dockerfile type:module + explicit missing-secret tripwire
Addresses two eighth-round review findings on PR #3157. 1. ESM .js files parse as CommonJS in the container (todo 232). Dockerfile.digest-notifications COPYs shared/*.js, server/_shared/*.js, api/*.js — all ESM because the repo-root package.json has "type":"module". But the image never copies the root package.json, so Node's nearest-pjson walk inside /app/ reaches / without finding one and defaults to CommonJS. First `export` statement throws `SyntaxError: Unexpected token 'export'` at startup. Fix: write a minimal /app/package.json with {"type":"module"} early in the build. Avoids dragging the full root package.json into the image while still giving Node the ESM hint it needs for repo-owned .js files. 2. Missing BRIEF_URL_SIGNING_SECRET silently tolerated (todo 233). The old gate folded "operator-disabled" (BRIEF_COMPOSE_ENABLED=0) and "required secret missing in rollout" into the same boolean via AND. A production deploy that forgot the env var would skip brief compose without any failure signal — Railway green, no briefs, no CTA in digests, nobody notices. Split the two states: BRIEF_COMPOSE_DISABLED_BY_OPERATOR (explicit kill switch, silent) and BRIEF_SIGNING_SECRET_MISSING (the misconfig we care about). When the secret is missing without the operator flag, composeBriefsForRun returns composeFailed=1 on first call so the end-of-run exit gate trips and Railway flags the run red. Digest send still proceeds — compose-layer issues never block notifications. Tests: 98/98. Syntax + node --check clean.
This commit is contained in:
@@ -32,6 +32,15 @@ FROM node:22-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# The repo-root package.json has "type":"module" which tells Node to
|
||||
# parse .js files under shared/, server/_shared/, api/ as ESM. Inside
|
||||
# this image we don't ship the full root package.json (it would pull
|
||||
# in dev-deps metadata we don't need), but the .js files we DO ship
|
||||
# still need the nearest-pjson walk to resolve to an ESM declaration.
|
||||
# A minimal /app/package.json avoids "SyntaxError: Unexpected token
|
||||
# 'export'" at container startup.
|
||||
RUN printf '{"type":"module","private":true}\n' > /app/package.json
|
||||
|
||||
# Install scripts/ runtime dependencies (resend, convex, etc.).
|
||||
COPY scripts/package.json scripts/package-lock.json ./scripts/
|
||||
RUN npm ci --prefix scripts --omit=dev
|
||||
|
||||
@@ -82,8 +82,15 @@ const WORLDMONITOR_PUBLIC_BASE_URL =
|
||||
process.env.WORLDMONITOR_PUBLIC_BASE_URL ?? 'https://worldmonitor.app';
|
||||
const BRIEF_TTL_SECONDS = 7 * 24 * 60 * 60; // 7 days
|
||||
const INSIGHTS_KEY = 'news:insights:v1';
|
||||
|
||||
// Operator kill switch — used to intentionally silence brief compose
|
||||
// without surfacing a Railway red flag. Distinguished from "secret
|
||||
// missing in a production rollout" which IS worth flagging.
|
||||
const BRIEF_COMPOSE_DISABLED_BY_OPERATOR = process.env.BRIEF_COMPOSE_ENABLED === '0';
|
||||
const BRIEF_COMPOSE_ENABLED =
|
||||
process.env.BRIEF_COMPOSE_ENABLED !== '0' && BRIEF_URL_SIGNING_SECRET !== '';
|
||||
!BRIEF_COMPOSE_DISABLED_BY_OPERATOR && BRIEF_URL_SIGNING_SECRET !== '';
|
||||
const BRIEF_SIGNING_SECRET_MISSING =
|
||||
!BRIEF_COMPOSE_DISABLED_BY_OPERATOR && BRIEF_URL_SIGNING_SECRET === '';
|
||||
|
||||
// ── Redis helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -898,6 +905,17 @@ function injectBriefCta(html, magazineUrl) {
|
||||
*/
|
||||
async function composeBriefsForRun(rules, nowMs) {
|
||||
const briefByUser = new Map();
|
||||
// Missing secret without explicit operator-disable = misconfigured
|
||||
// rollout. Count it as a compose failure so the end-of-run exit
|
||||
// gate trips and Railway flags the run red. Digest send still
|
||||
// proceeds (compose failures must never block notification
|
||||
// delivery to users).
|
||||
if (BRIEF_SIGNING_SECRET_MISSING) {
|
||||
console.error(
|
||||
'[digest] brief: BRIEF_URL_SIGNING_SECRET not configured. Set BRIEF_COMPOSE_ENABLED=0 to silence intentionally.',
|
||||
);
|
||||
return { briefByUser, composeSuccess: 0, composeFailed: 1 };
|
||||
}
|
||||
if (!BRIEF_COMPOSE_ENABLED) return { briefByUser, composeSuccess: 0, composeFailed: 0 };
|
||||
|
||||
let insightsRaw = null;
|
||||
|
||||
Reference in New Issue
Block a user