Commit Graph
2 Commits
Author SHA1 Message Date
yuriy.pandClaude Opus 5 b673367c6f Prepare the four landings for production deployment
Fixes that block or weaken a real deployment. Nothing here changes the
rendered pages.

Bind the lead API to loopback. Each server called app.listen() without a
host, so it bound 0.0.0.0. Combined with a blanket `trust proxy: true` —
which makes Express take the leftmost X-Forwarded-For entry as req.ip —
the in-memory lead rate limiter was spoofable by anyone who could reach
the port directly. HOST now defaults to 127.0.0.1 and trust is narrowed
to 'loopback', so a request arriving from anywhere but the local proxy
has its forged header ignored.

Give each landing its own port. All four .env files claimed PORT=3000,
and fitnes/.env.example collided with medcenter/.env.example, so three of
the four could never have started on one host. Now 3000/3001/3002/3003
consistently across the code defaults, the env templates and the vite
dev proxies, so all four also run side by side locally.

Template the JSON-LD url. canonical and og:url already resolved from
%VITE_SITE_URL%, but the JSON-LD block hardcoded an exodevices.ru
sub-path that would not follow the environment. All four now read from
the same variable.

Declare the Node version. Nothing stated it, yet transitive deps impose
a >=22.12 floor (@rolldown/binding, yargs, concurrently). Added engines
and .nvmrc so a too-old runtime fails clearly.

Typechecked and production-built on all four; verified the socket binds
127.0.0.1 only, health reports amo:true, the site still boots with the
CRM unconfigured, and the limiter returns 429 with Retry-After on the
ninth request.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 15:57:24 +06:00
Yuriy PanovandClaude Fable 5 cd3414d8d0 Convert "physiotherapy from scratch" landing to React + Tailwind v4
Ports the last single-file page, MedCbezFiz — the medical centre that has no
physiotherapy yet — to the architecture the other three landings now share:
Vite + React 19 + TypeScript + Tailwind v4, Express 5 for the API, zod schema
shared between client and server.

The original is kept in legacy/index.html as the visual reference, with its
instructions alongside it in legacy/README.md. Its 4 MB collapse to 317 KB of
JS plus assets loaded on demand. Fourteen inlined images were extracted; twelve
turned out byte-identical to the medcenter landing's, and the two large device
PNGs (1.5 MB and 750 KB) had identical sources, so the webp conversions from
that landing are reused rather than redone — 664 KB of assets instead of 2.8 MB.

Text was verified rather than eyeballed: the app is server-rendered to static
HTML and its visible text diffed against the legacy file. 280 blocks against
281, and the only difference is the honeypot's screen-reader label, which the
original has no equivalent for.

Two things in the original are dead and were deliberately not ported: 39
.patient-economics / .base-economics-card rules that no element uses, and the
[data-counter] animation handler, which likewise matches nothing on the page.
Both are documented in the README.

Departures from the medcenter landing this was built from, all following the
original: the hero photo is anchored at 65% (72% from 980px), the header is
transparent until 18px of scroll instead of always painted, the revenue band
keeps 32px gutters at every width, and the launch timeline breaks into two
columns at 900px rather than 760px.

Leads post to /api/leads/medical-centers-no-physio, the address the original
page already referenced, into pipeline 10980758. The cabinet_state select has
no counterpart on this form and was dropped from the schema, mapper and check
script. Vite runs on 5175 and the API on 3002 so all four landings can run at
once.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 19:59:29 +06:00