Next.js frontend for FermatMind assessments.
- Primary content routes:
/[locale]/articles/[locale]/career/[locale]/help/[locale]/business
- Legacy routes are preserved with permanent redirects:
/[locale]/blog*->/[locale]/articles*/[locale]/support->/[locale]/help/[locale]/professions*->410 Gone/[locale]/types*->410 Gone
- Node.js 24.x (see
.nvmrc) - pnpm 10.28.1 (via
corepack)
This repository is pnpm-only.
- Use:
pnpm install --frozen-lockfile - Do not use:
npm install/yarn install
- This repository only supports Node.js 24.x and pnpm.
pnpm installruns a runtime check and fails fast on the wrong Node major version or a non-pnpm installer.- Run
nvm usebefore installing dependencies if your shell is not already on Node 24.x.
corepack enable
pnpm run check:runtime
pnpm install --frozen-lockfile
pnpm devpnpm lint
pnpm lint:spacing
pnpm typecheck
pnpm test:contract
pnpm test:a11y
pnpm test:e2e tests/e2e/home-visual.spec.ts tests/e2e/tests-list.spec.ts tests/e2e/test-detail.spec.ts
pnpm test:e2e tests/e2e/sds-flow.spec.ts tests/e2e/clinical-combo-flow.spec.ts tests/e2e/big5-flow.spec.ts tests/e2e/big5-negative.spec.ts
pnpm build
# Visual snapshots are Linux-baseline only and always run full visual suite.
pnpm test:e2e:visual:ci
# Update snapshots after intentional UI changes (full suite only).
pnpm test:e2e:visual:update
# Update snapshots in Linux container (authoritative for PRs, full suite only).
pnpm test:e2e:visual:update:linux
pnpm release:gate
# Optional for local machines that keep .env.local:
RELEASE_GATE_ALLOW_LOCAL_ENV=1 pnpm release:gateRecommended CI order for UI changes:
pnpm lintpnpm lint:spacingpnpm test:e2e:visual:update(orpnpm test:e2e:visual:update:linux)pnpm test:e2e:visual:ci
Visual snapshot policy:
- Only
*-linux.pngbaselines are supported undertests/e2e/visual/*-snapshots/. - Do not commit
*-darwin.pngfiles. - Any intentional UI change must include updated Linux visual snapshots in the same PR.
- Do not update a single visual spec in isolation; always run full
tests/e2e/visual. - Visual runner ignores local
.env.local/.env.production.localby default to match CI. UseVISUAL_USE_LOCAL_ENV=1only when explicitly needed. - Playwright now starts its own server by default (no silent port-3000 reuse). If you intentionally want to reuse an existing local server, set
PLAYWRIGHT_REUSE_SERVER=1.
Release operation details and rollback thresholds are documented in:
/Users/rainie/Desktop/GitHub/fap-web/docs/ui-unification-release-runbook.md
- New visitor bootstrap:
- Clear browser
localStorageand cookies for the site. - Open a
/tests/[slug]/takepage. - Confirm
fap_anonymous_id_v1exists in both cookie andlocalStorage, and outbound API requests includeX-Anon-Id.
- Clear browser
- Legacy identity migration:
- Set
localStorage.fap_anonymous_id_v1to an existing value. - Set cookie
fap_anonymous_id_v1to a different value. - Reload and confirm cookie is overwritten by the
localStoragevalue.
- Set
- Order polling backoff:
- Open
/orders/{orderNo}while backend status remainspending. - Confirm polling interval follows
2s -> 3s -> 5s -> 8s -> 10sand manual refresh triggers immediate poll.
- Open
- Paid auto redirect:
- Return
paidplusattempt_idfrom order status. - Confirm page auto navigates to
/result/{attemptId}.
- Return
- Paid but report not ready:
- Return
paidwithoutattempt_idon order status, orgenerating=trueon report API. - Confirm UI shows generating state and retries using
retry_after(fallback 3s, capped at 10s).
- Return
- Sensitive route caching:
- Visit
/orders/{orderNo}and/result/{attemptId}with different values. - Confirm responses are dynamic and do not leak stale/cross-user content.
- Visit
pnpm build
pnpm startCurrent runtime standard is Node 24.x.
Node1 production authority remains PM2, using /usr/bin/node as the tracked runtime path for /opt/apps/fap-web/.next/standalone/server.js under the Node 24.x standard.
The current production deploy entrypoint is /Users/rainie/Desktop/GitHub/fap-web/scripts/deploy_web_pm2.sh.
On Node1, fap-web.service is currently absent; that is not an anomaly and does not block the PM2-backed production topology.
Current production authority assets are:
/Users/rainie/Desktop/GitHub/fap-web/scripts/deploy_web_pm2.sh/Users/rainie/Desktop/GitHub/fap-web/ecosystem.config.cjs/Users/rainie/Desktop/GitHub/fap-web/deploy/nginx/fap-web.conf
Fallback/reference assets are:
/Users/rainie/Desktop/GitHub/fap-web/deploy/systemd/fap-web.service/Users/rainie/Desktop/GitHub/fap-web/docs/deploy/systemd-fap-web.service
/Users/rainie/Desktop/GitHub/fap-web/docs/deploy/* remain reference docs.
For cron autoheal setup under the current PM2 authority, see:
/Users/rainie/Desktop/GitHub/fap-web/docs/deploy/502-recovery-runbook.md/Users/rainie/Desktop/GitHub/fap-web/docs/deploy/pm2-autoheal-cron.md
The deploy entrypoint fails fast unless both the shell node and /usr/bin/node resolve to Node 24.x.
For the current Node1 production host, which node and /usr/bin/node must resolve to the same Node 24 runtime contract.
Use a single deploy entrypoint to avoid malformed multi-line PM2 commands.
Do not run hand-typed PM2 start commands such as pm2 start ... -- \\ -lc ....
PM2 remains the only current production app process authority on Node1.
pnpm run deploy:pm2
# or:
bash scripts/deploy_web_pm2.shEnvironment overrides (optional): APP_DIR, APP_NAME, APP_USER, APP_HOST, APP_PORT, PUBLIC_BASE_URL, CORE_PUBLIC_PATH, GIT_BRANCH.
Run once on the server to enable PM2 resurrection after reboot:
pm2 startup systemd -u ubuntu --hp /home/ubuntu
pm2 saveThe systemd interaction above is for PM2 resurrection, not for a standalone fap-web.service authority on Node1.
Healthcheck script:
bash scripts/healthcheck_web.shExit codes: 0=healthy, 1=local endpoint failure, 2=public endpoint failure, 3=pm2 process failure.
Autoheal script:
WECOM_BOT_WEBHOOK="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=<your-key>" \
bash scripts/autoheal_pm2.shSupported env vars:
WECOM_BOT_WEBHOOKHEALTHCHECK_LOCAL_URLS(default:http://127.0.0.1:3000/en,http://127.0.0.1:3000/zh)HEALTHCHECK_PUBLIC_URLS(default:https://fermatmind.com/en,https://fermatmind.com/zh)AUTOHEAL_COOLDOWN_SEC(default:600)
Cron example (every minute):
* * * * * cd /opt/apps/fap-web && WECOM_BOT_WEBHOOK="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=<your-key>" bash scripts/autoheal_pm2.sh >> /var/log/fap-web-autoheal.log 2>&1If a host explicitly installs the tracked fallback unit, keep it aligned with /usr/bin/node on Node 24.x and .next/standalone/server.js.
Do not treat that fallback/reference unit as the current Node1 production authority.
pnpm build
node .next/standalone/server.js