Skip to content

Fix environment separation - #208

Merged
karthikeya1976 merged 17 commits into
mainfrom
signed-environment-separation
Sep 16, 2026
Merged

karthikeya1976 merged 17 commits into
mainfrom
signed-environment-separation

Conversation

@sweksha-cloud

@sweksha-cloud sweksha-cloud commented Aug 27, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Separates dev/prod environment configuration across the frontend and all backend services, and adds startup validation so a misconfigured deployment fails loudly instead of silently. While working through CORS-related config in each service, also found and fixed an unsafe wildcard+credentials combination and an unhandled-500 bug on rejected CORS origins.

Type of Change

  • New feature
  • Bug fix
  • Refactor
  • Documentation update
  • Style/UI update
  • Performance improvement
  • Other (please specify):

Key Changes

  • middlewareNode: adds config/default.js and .env.example, replaces the old custom-environment-variables.json approach, adds validateEnvironment.js to check required vars at startup, updates db.js and server.js accordingly
  • chessServer / stockfishServer: adds .env.example and validateEnvironment.js for the same startup validation
  • react-ystemandchess: environment.prod.js now supports REACT_APP_* build-time overrides (REACT_APP_MIDDLEWARE_URL, REACT_APP_STOCKFISH_SERVER_URL, REACT_APP_CHESS_SERVER_URL, REACT_APP_AGORA_APP_ID), falling back to existing placeholders when unset; fixes several components importing from the old environments/environment path instead of environments
  • CI (.github/workflows/ci.yml): build step now feeds these REACT_APP_* vars into the production frontend build (previously written to a path nothing in the app imports, so they never reached the built app); adds a workflow_dispatch trigger for manual runs
  • deploy/dev and deploy/prod docker-compose: updated to match

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • All tests pass

Test counts (updated after review commits): React 156/156, chessServer 33/33, stockfishServer 15/15 (was 12/12; +3 CORS tests added during review), middlewareNode 264/264 — no regressions.

Bugs Fixed (if applicable)

  • chessServer/src/index.js and stockfishServer/src/index.js: credentials: true was unconditional, even when allowedOrigins includes "*" — an unsafe wildcard+credentials combination. Now computes hasWildcard and sets credentials: !hasWildcard.
  • middlewareNode/src/server.js: callback(new Error(...)) in the CORS origin function produced an unhandled 500 (leaking a stack trace outside production) on any disallowed-origin request, since no error-handling middleware is registered. Changed to callback(null, false) to match the pattern already used elsewhere. Added middlewareNode/tests/cors.test.js and chessServer/src/tests/cors.test.js to cover this going forward.

TODO (Follow-up Work)

  • Consolidate the duplicated CORS allowlist/wildcard logic across chessServer and stockfishServer into a shared module — currently blocked on each service's Docker build context being scoped to its own directory, so this would mean restructuring the build too
  • Confirm whether dropping chessClientURL from environment.prod.js is intentional — confirmed accidental (traced to a stale "unused code" analysis that predated PlayStudent.tsx's actual usage of it). Restored as an optional build-time var (REACT_APP_CHESS_CLIENT_URL) in a2fcbec4, wired through the Dockerfile, tag_build_containers.sh, and CI. The TODO(merge-chessclient-refactor) comment in PlayStudent.tsx itself is unrelated follow-up work and still stands.

Additional Notes

Replaces #200 — same underlying changes, rebased onto the latest main and squashed into one SSH-signed commit to satisfy the branch's signed-commit requirement. fix/environment-separation is left untouched for reference to the original commit history and review discussion.

@karthikeya1976 karthikeya1976 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff and ran every affected suite for real (backend 264/264, chessServer 33/33, stockfishServer 12/12, frontend 156/156, TypeScript clean) against a local worktree with the real gitignored config alongside the new files — not just re-reading the PR's own claimed numbers.

Real, well-reasoned fixes confirmed:

  • The environments/environment → environments import-path fix (~30 files) is a genuine, previously-silent bug: importing environment.js directly bypasses environment.prod.js entirely, so every one of these components was using hardcoded dev URLs in any real production build. Confirmed src/core/environments/ (the old CI step's write target) doesn't exist anywhere in the tree — that secrets-injection step really was a no-op.
  • db.js: refusing to fall back to in-memory MongoDB in production, and skipping test-user seeding in production, are both real safety fixes — the old behavior meant a prod DB outage could've silently switched the whole app to an ephemeral, fake-seeded database with no visible signal beyond a log line.
  • CORS wildcard+credentials:true fix in chessServer/stockfishServer is correct, and the new tests actually exercise the previously-broken case (disallowed origin now returns 200 instead of an unhandled 500 with a leaked stack trace).
  • Checked whether dropping credentials from middlewareNode's cors() call breaks anything: it doesn't. The frontend never sends credentials: "include" anywhere (see badgesApi.ts's own comment on why) — auth is carried via a Bearer header end to end, so no cross-origin cookie flow depends on it.
  • Verified the config/default.js + config/default.json coexistence empirically (own test harness) rather than assuming: node-config loads both and default.json (the real, gitignored secrets file) wins on any overlapping key, so no risk of default.js's placeholder empty strings clobbering real production secrets.

One thing worth flagging before merge, not necessarily blocking:
deploy/prod/docker-compose.yml's env var names changed substantially (mongoURI→MONGO_URI, indexKey→INDEX_KEY, jwtSecret dropped, auth/channel dropped, several renamed). Combined with the new validateEnvironment.js calling process.exit(1) on any missing required var, this means the actual production server's env/secrets file needs a coordinated manual update at deploy time, or the middleware container will refuse to boot after this ships. I didn't see that called out explicitly in the PR description or a TODO — worth a one-line deployment note (or confirmation it's already been coordinated) so whoever deploys this isn't surprised by a crash-looping container.

Everything else — CI workflow secrets fix, .env.example files, validateEnvironment additions — is accurate and matches what's actually wired up in the code (spot-checked CHAT_RATE_LIMIT against routes/chat.js as one example).

Nice find on the dead CI step and the prod-fallback DB issue — both are the kind of bug that's invisible until it actually happens in production.

karthikeya1976
karthikeya1976 previously approved these changes Aug 27, 2026

@karthikeya1976 karthikeya1976 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving the backend/infra portions I own as codeowner (middlewareNode, chessServer, stockfishServer, deploy) — already reviewed in detail in my prior comment, all 5 test suites verified passing in an isolated worktree, CORS/config-merge/prod-fallback claims verified empirically rather than assumed. One non-blocking note left in that comment: confirm the production env/secrets file is updated in lockstep with the docker-compose.yml var renames before deploying, since validateEnvironment.js will now exit(1) on any missing required var.

This PR also touches react-ystemandchess (the environments import-path fix across ~30 components, and the Dockerfile), which is Sarita's codeowner scope — that portion still needs her review/approval before this can merge.

@Saritahimthani

Copy link
Copy Markdown
Collaborator

Two concrete blockers from my review:

The indexKey: "" default breaks login in any dev/test environment without INDEX_KEY set (verified: jwt.sign throws on the empty secret, auth.js swallows it into a generic 500). That's a real, reproducible bug, not a style nit.
It's a near-duplicate of #200 (same branch lineage, byte-identical files) — approving #208 while #200 is still open means you'll likely end up merging the same work twice or creating a fresh conflict. Worth getting a decision on which one is the "real" PR before either gets approved.

@Saritahimthani Saritahimthani left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given INDEX_KEY is empty by default in middlewareNode/config/default.js, logging in locally throws (jwt.sign rejects an empty secret) whenever INDEX_KEY isn't set in the environment — verified with tests/routeSecurity.test.js, which currently fails with a 500 instead of 200 on POST /auth/login.

Can you apply this fix?

diff --git a/middlewareNode/config/default.js b/middlewareNode/config/default.js
index 5864fdf8..7e3826e9 100644
--- a/middlewareNode/config/default.js
+++ b/middlewareNode/config/default.js
@@ -1,7 +1,11 @@
module.exports = {
mongoURI: "",
jwtSecret: "",

  • indexKey: "",
  • // Non-empty so jwt.sign() (used for login) doesn't throw "secretOrPrivateKey

  • // must have a value" in dev/test when INDEX_KEY isn't set. Every real

  • // deployment overrides this via INDEX_KEY — validateEnvironment.js

  • // requires it in production.

  • indexKey: "dev-index-key-replace-in-prod",

    corsOptions: {
    origin: "http://localhost:3000",

-> Same placeholder value deploy/dev/docker-compose.yml already uses, so it's consistent with the rest of the repo. I verified this locally — with the change applied, the full middlewareNode suite goes from 1 failing to 264/264 passing. Once this is in, I'm good to approve.

@sweksha-cloud
sweksha-cloud force-pushed the signed-environment-separation branch from ef7c765 to 59cadc8 Compare August 30, 2026 11:47
indexKey: "" caused jwt.sign()/jwt.verify() to throw whenever INDEX_KEY
wasn't set, breaking login, passport JWT auth, token verification, and
password-reset token generation in dev/test.

Uses a random key generated once per process boot instead of a fixed
fallback literal, so no signing key is ever checked into git history.
custom-environment-variables.json already overrides this from INDEX_KEY
when set; validateEnvironment.js still requires INDEX_KEY in production.
@sweksha-cloud

Copy link
Copy Markdown
Collaborator Author

Reworked the indexKey fix based on internal review — went with a random per-boot key instead of a hardcoded fallback literal.

Why the change: the originally proposed indexKey: "dev-index-key-replace-in-prod" is a fixed string that would live in git history permanently — the same category of issue as #215's "development-jwt-secret-key" fallback (checked into both default.js and auth.js there). Any deployment that forgets to set INDEX_KEY would silently sign JWTs with a secret visible to anyone with repo access.

What it does instead: config/default.js now generates a random 32-byte key via crypto.randomBytes(32) once per process boot, used only as the last-resort fallback when INDEX_KEY isn't set anywhere else. custom-environment-variables.json already maps INDEX_KEY → indexKey, so real deployments (or a local config/default.json) override this automatically — no behavior change there. validateEnvironment.js still hard-requires INDEX_KEY in production, so this fallback is dev/test-only by construction.

When it's in use, a console.warn logs that fact (not the key value) so it's visible in dev/test output.

Scope note: config.get("indexKey") is read in five places, not just login — auth.js (sign), passport.js (JWT strategy verify), middleware.js, users.js (verify), and changePasswordTemplate.js (password-reset token sign). All five were equally broken by the empty-string default, so this fixes password-reset and passport-protected routes locally too, not just POST /auth/login.

Tradeoffs worth knowing:

  • Tokens signed with the fallback stop verifying on process restart, since a fresh key is generated each boot. In practice that means nodemon (which npm start uses here) invalidates every live session on any file save — a real dev-workflow annoyance, not just an edge case.
  • Same restart sensitivity applies to password-reset links specifically: if the dev server restarts between "email sent" and "link clicked," the reset link breaks. Not a regression (it was unconditionally broken before), but not fully solved either — now intermittent instead of guaranteed-broken.
  • validateEnvironment.js only hard-requires INDEX_KEY when NODE_ENV === "production" exactly. Any differently-named environment (staging, qa, etc.) now silently gets an ephemeral key with just a console.warn — before this fix, that same misconfiguration would've hard-crashed on login instead of failing silently. Worth confirming this gate actually covers every environment we deploy to, alongside the auto-deploy/root-docker-compose.yml questions already open on this PR.
  • If this process is ever run with multiple workers/replicas in a non-production environment without INDEX_KEY set, each process gets its own random key, so tokens won't verify across instances behind a load balancer.

Verified: npm test → 264/264 passing, including routeSecurity.test.js's login case going from failing to passing.

Mirrors chessServer/src/tests/cors.test.js's three cases (allowed
origin, disallowed origin rejected without a 500, wildcard disables
credentials) against stockfishServer's identical corsOptions logic,
which had no dedicated coverage. Adds supertest as a devDependency
since it wasn't installed anywhere in this service.
The component's import moved to "../../../../environments" (the fixed
path every other file in this PR moved to), but this test's jest.mock
still targeted the old "../../../../environments/environment" path.
The mock was silently not intercepting anything — the test passed by
coincidence against the real dev environment module, not the mocked
one. Fetch calls in LeaderboardModal.tsx build URLs from
environment.urls.middlewareURL, so this now genuinely exercises the
mock.
… rewrite

chessClientURL existed on main but was silently dropped in an earlier
commit (228ee3f) that was actually about renaming chessServer ->
chessServerURL. Traced it back to a prior analysis that claimed the
key was unused; that claim was already false when it was written --
PlayStudent.tsx has read environment.urls.chessClientURL since three
weeks before that analysis ran, via an `as any` cast that likely hid
it from type-aware reference tooling.

Restored as optional (falls back to '' if REACT_APP_CHESS_CLIENT_URL
is unset), not required like the other three URLs -- PlayStudent.tsx
already degrades gracefully on a missing value, and making this one
required would turn a single broken button into an app-wide crash for
every visitor. Wired REACT_APP_CHESS_CLIENT_URL through the Dockerfile,
tag_build_containers.sh, and ci.yml (using the existing CHESS_CLIENT_URL
secret) to match the other three URLs' plumbing.

Verified empirically: a real production build succeeds with the var
unset (no crash), and the value is correctly inlined into the bundle
when it is set. Frontend suite still 156/156, TypeScript clean.
@karthikeya1976

Copy link
Copy Markdown
Collaborator

Independently verified Sweksha's random-per-boot `indexKey` fix (commit on `config/default.js`, currently HEAD `a2fcbec4`) against Sarita's originally-reported bug:

  • Ran the full backend suite in a clean worktree with no `config/default.json` present at all (no local secrets, closest thing to a truly fresh checkout): 264/264 passing, matching both of your reported counts.
  • Specifically isolated the exact case Sarita flagged (`routeSecurity.test.js`, `POST /auth/login -> 200 with JWT token when valid credentials provided in JSON body`): passes cleanly, confirmed independently.

So the random-key approach does fully resolve the reported bug, not just coincidentally pass the suite.

Also confirmed #200 is now closed — that resolves the duplicate-PR concern from your first comment; #208 is the sole surviving PR for this work.

The one open item from Sarita's Aug 28 comment is whether the random-per-boot approach (vs. the hardcoded placeholder originally proposed) is acceptable given the tradeoffs Sweksha wrote up (session invalidation on every `nodemon` restart, password-reset links breaking across a restart, and the staging/qa environments only getting a `console.warn` instead of a hard failure). Those are real dev-workflow costs worth a explicit yes/no rather than assuming silence means agreement — flagging so this doesn't sit waiting on an unstated question.

CI is green on the current HEAD.

@sweksha-cloud

sweksha-cloud commented Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

Thanks for the independent verification — good to have the duplicate-PR question closed out too.

Also just pushed a merge commit (8280fc5) resolving a conflict with main (#207's stockfish crash-permission fix landed and touched the same area of stockfishServer/src/index.js) — kept both changes, no logic lost, 16/16 stockfishServer tests passing. Heads up that this dismissed your approval again, so it'll need a re-look.

On the indexKey open item: since Sarita's out for a week and the tradeoff question is really about whether the random-per-boot approach is acceptable engineering-wise (not something that needs her specifically — that file's in your codeowner scope, not hers), can I get your explicit yes/no on it directly? You've already reviewed both versions in depth, so you're in a good spot to make that call without waiting on her.

To recap the tradeoffs for a quick decision:

  • Sessions invalidate on every nodemon restart (fresh key each boot)
  • Password-reset links can break if the server restarts between "email sent" and "link clicked" (intermittent now, was guaranteed-broken before)
  • Environments other than exactly NODE_ENV=production only get a console.warn, not a hard failure, if INDEX_KEY isn't set

If it's a no, let me know what you'd want instead.

@karthikeya1976 karthikeya1976 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

Solid overall direction — separating dev/prod CORS config, adding env validation with fail-fast, and adding CORS regression tests is the right call. A few things I'd like addressed before merge, plus some lower-priority follow-ups.

Please fix before merge

  1. CI never runs middlewareNode's new tests (.github/workflows/ci.yml)
    middlewareNode/tests/cors.test.js is added and npm test is wired up correctly in its package.json, but the CI workflow only has Install/Build steps for middlewareNode — no Test step (unlike chessServer/stockfishServer, which both have one). The new CORS test never actually runs, so it can't catch regressions in the logic it exists to protect.

  2. middlewareNode's CORS rewrite silently drops credentials (middlewareNode/src/server.js)
    The old code forwarded the whole corsOptions config object (including credentials: true) into cors(). The new inline callback only reads .origin, so credentials now defaults to false and Access-Control-Allow-Credentials is never sent — unlike chessServer/stockfishServer's equivalent rewrite, which explicitly keeps a credentials: !hasWildcard interlock. Currently latent (no frontend call uses credentials: "include" against middleware today), but it's a silent capability regression with nothing testing for it.

  3. Production Mongo failure now hard-exits using a timeout tuned for the old fallback path (middlewareNode/src/config/db.js)
    process.exit(1) on connection failure is right for prod, but it still uses serverSelectionTimeoutMS: 1000 (1s), which was tuned for "fail fast to a harmless in-memory fallback," not "fail fast and kill the process." A brief network blip or Atlas cold-start during a deploy could now crash-loop the container. Worth bumping the timeout (or adding a couple of retries) for the production path specifically.

  4. deploy/prod/tag_build_containers.sh never version-tags the middleware image
    chessserver/stockfishserver/ystemandchess all build as :${TAG}; middlewarenode always builds as bare :latest. A versioned release/rollback (TAG=v1.2.0) silently leaves middleware on whatever latest currently is, breaking rollback consistency across the four services.

Worth a follow-up (not blocking)

  • CORS rejection changed from a thrown Error (→ 500 + logged error) to callback(null, false) (→ silent 200, no CORS header, no log line) across all three servers. Looks intentional per the new tests, but it's a real observability regression for diagnosing a misconfigured CORS_ORIGIN in production — worth at least a log line on rejection.
  • CORS + env-validation logic is now implemented three separate times (chessServer, stockfishServer, middlewareNode) with no shared module, and it's already drifted (see #2 above, and middlewareNode's validateEnvironment requires CORS_ORIGIN unconditionally while the other two accept ALLOWED_ORIGINS as a fallback). Might be worth extracting into a shared helper at some point so this doesn't keep drifting.
  • chessServer/stockfishServer require("cors") without declaring it in their own package.json (pre-existing, but the new test files deepen the reliance on it resolving via hoisting).
  • No validation that CORS_ORIGIN entries are well-formed (e.g. a trailing slash would silently never match a browser's Origin header).
  • tag_build_containers.sh uses set -e but not set -u; an unset REACT_APP_* var passes through as an empty string to docker build and fails confusingly deep inside the CRA build instead of failing fast at the shell level.

Nice addition of test coverage for CORS behavior — just want CI actually running it and the credentials gap closed before this goes out.

@sweksha-cloud
sweksha-cloud force-pushed the signed-environment-separation branch from 45bc67c to 8280fc5 Compare September 4, 2026 22:27
sweksha-cloud and others added 5 commits September 4, 2026 15:31
The workflow had Install/Build steps for middlewareNode but no Test
step, unlike chessServer/stockfishServer, so its test suite (including
the new cors.test.js) never actually ran in CI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xHcrGS7T6Kp4vDif3mYc8
chessserver/stockfishserver/ystemandchess all build as :${TAG}, but
middlewarenode always built as bare :latest, so a versioned release
(TAG=v1.2.0) silently left it on whatever :latest happened to be,
breaking rollback consistency across the four services.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xHcrGS7T6Kp4vDif3mYc8
Production now exits the process on a failed connection instead of
falling back to in-memory MongoDB, but it still used the 1s timeout
that was tuned for "fail fast to a harmless fallback" rather than
"fail fast and kill the process." A brief network blip or an Atlas
cold-start during deploy could crash-loop the container. Dev/test keep
the fast 1s timeout since they still fall back to in-memory MongoDB.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xHcrGS7T6Kp4vDif3mYc8
The CORS rewrite's inline origin callback only read .origin from
config, silently dropping the credentials: true that config/default.json
sets for real deployments (the old code forwarded the whole corsOptions
object into cors(), so credentials worked there). Mirrors the
credentials: !hasWildcard interlock already used in chessServer and
stockfishServer: allow credentials only when a specific origin (not "*")
is configured, since wildcard + credentials is an unsafe CORS
configuration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xHcrGS7T6Kp4vDif3mYc8
The CORS rewrite changed rejections from a thrown Error (500 + logged
error) to callback(null, false) (silent 200, no CORS header, no log
line). That's the correct HTTP behavior, but it's an observability
regression: a misconfigured CORS_ORIGIN in production now fails
silently with nothing in server logs to point at the cause.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xHcrGS7T6Kp4vDif3mYc8
Both services only had cors available because it's a transitive
dependency of socket.io, hoisted to the top of node_modules by
chance. If socket.io's own dependency tree ever changes, require("cors")
could break with no change to either service's own package.json.
Pin the same version middlewareNode already declares (^2.8.5).
set -e alone didn't catch an unset REACT_APP_* var — it would pass
through as an empty string to docker build and fail confusingly deep
inside the CRA build instead of failing fast at the shell level.
A configured origin like https://ystemandchess.com/ (with a trailing
slash) would never match a browser's Origin header, which never
includes one — silently breaking CORS for that origin with no
indication why. Strip the trailing slash and warn when it happens.
Generate the fallback key once and persist it to a gitignored local file (config/.dev-index-key) instead of regenerating it every process
boot. Keeps production unchanged (validateEnvironment.js still hard-requires INDEX_KEY there, and runs before this file is ever loaded)
and avoids committing a key, while no longer invalidating dev JWTs and password-reset links on every nodemon restart.
@sweksha-cloud

Copy link
Copy Markdown
Collaborator Author

Pushed fixes for everything from the 9/4 review, plus the indexKey follow-up. Summary of everything since 8280fc5:

Blocking items addressed:

  • fa7acd5 — Added a "Test middlewareNode" step to CI; cors.test.js existed but I'd never actually wired it into the workflow.
  • e0fc004 — Fixed tag_build_containers.sh to tag the middlewareNode image as middlewarenode:${TAG} instead of bare :latest.
  • 779f27c — Raised the production Mongo connect timeout from 1s to 10s in db.js, since a failure there now exits the process instead of falling back.
  • 9129145 — Restored credentials: !hasWildcard in middlewareNode's CORS config, matching chessServer/stockfishServer.

Non-blocking follow-ups, also addressed:

  • 839b668 — Rejected CORS origins now get logged in all three servers (were silently returning 200 with no log line).
  • 3dc6b6d — Declared cors as a direct dependency in chessServer/stockfishServer's own package.json instead of relying on it getting hoisted from socket.io.
  • cfed055 — Added set -u to tag_build_containers.sh, so a missing REACT_APP_* build arg fails fast instead of building a broken image silently.
  • e613f1b — Normalized trailing slashes on configured CORS origins (with a warning), since a browser's Origin header never has one.

On indexKey — went with Sahana's call (persist-to-gitignored-file): 81992ea now generates the dev/test fallback key once and persists it to config/.dev-index-key instead of regenerating every boot. Production's unaffected — validateEnvironment.js still hard-requires INDEX_KEY there and runs before this file is ever loaded.

I'm leaving the shared CORS/env-validation module extraction as a separate ticket rather than another commit here — it's a real refactor that also needs a Docker build-context decision, not a drop-in fix.

All three test suites passing after every change (middlewareNode 264/264, chessServer 33/33, stockfishServer 16/16). Ready for re-review whenever you get a chance.

@karthikeya1976 karthikeya1976 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed after the Sept 4 changes-requested pass — all four blocking items are genuinely fixed, verified against the actual branch content (not just the commit messages):

  1. CI runs middlewareNode's tests — confirmed a Test middlewareNode step now exists in .github/workflows/ci.yml, and the latest CI run on this branch passes.
  2. credentials restored in middlewareNode's CORS — server.js now sets credentials: !hasWildcard, matching the interlock already used in chessServer/stockfishServer.
  3. Production Mongo timeout bumped — db.js now uses serverSelectionTimeoutMS: IS_PRODUCTION ? 10000 : 1000, so a brief network blip or Atlas cold-start won't crash-loop the container; dev/test keep the fast 1s timeout to their in-memory fallback.
  4. middlewareNode image is version-tagged — tag_build_containers.sh now builds middlewarenode:${TAG} instead of bare :latest.

Two of the non-blocking follow-ups were also addressed: CORS rejections are now logged (console.warn on reject), and a trailing slash on a configured origin is normalized with a warning instead of silently never matching.

Re-ran all three backend suites myself in an isolated worktree against the current branch head (d871e3ff), not just re-reading claimed numbers: middlewareNode 264/264, chessServer 33/33, stockfishServer 15/15 (1 pre-existing skip) — matches the PR's own reported counts exactly.

Remaining open items are genuinely non-blocking (shared CORS/env-validation module across the three servers, set -u in the build script) and are already tracked as follow-up work in the PR description. Approving the backend/infra portions I own as codeowner.

🤖 Generated with Claude Code

@karthikeya1976
karthikeya1976 merged commit 2fa6a96 into main Sep 16, 2026
1 check passed
Deepesh-Katudia added a commit that referenced this pull request Sep 25, 2026
Brings in #208 (environment separation), #247 (Tailwind standardization)
and the other changes merged since the branch was cut on 2026-08-23.

Conflicts in Lessons.tsx, ActivitiesModal.tsx and LeaderboardModal.tsx were
import-only: main's side is kept (SCSS modules dropped by #247, environment
imported from ../environments), with SVG components switched from CRA's
`{ ReactComponent as X }` to Vite's `X from './x.svg?react'`.
Deepesh-Katudia added a commit that referenced this pull request Sep 25, 2026
environment.prod.js (from #208) read vars via process.env[name]. Vite's
define is a literal text replacement and cannot resolve a dynamic key.
Vite 8 rewrites the leftover bare `process.env` to `{}`, so the lookup
compiled to `{}[name]`, which is always undefined. The required-URL guard
then threw on load and blanked the whole site. Jest runs in Node, so the
test suite could not catch it.

- environment.prod.js: reference every variable by its full literal name
- vite.config.mts: define all six REACT_APP_* vars the app reads, loaded
  via loadEnv so .env files work as they did under CRA; NODE_ENV derives
  from the Vite mode
- scripts/verify-build-env.mjs + CI: fail if process.env survives in
  shipped JS/HTML, or if a required URL was not inlined into the bundle
  (the second check is what actually catches the {}[name] case; verified
  it fails against main's old environment.prod.js and passes on this one)
- Dockerfile and volta pin: Node 20.19.0 (EOL) -> 24.21.0 LTS
Deepesh-Katudia added a commit that referenced this pull request Sep 25, 2026
The plan covers what changed after #208 merged (the env var blank-page bug,
merge conflicts, Node 24, password-reset URL), the prerequisite branches
that gate the merge, and coordination with the CD pipeline. The original
migration write-up stays as the reference for the migration itself.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants