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>
49 lines
2.6 KiB
HTML
49 lines
2.6 KiB
HTML
<!doctype html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
<title>ЭкзоОтель — пространство восстановления для гостей | Экзо Групп</title>
|
|
<meta
|
|
name="description"
|
|
content="ЭкзоОтель — готовое пространство восстановления внутри отеля: 4 аппарата, формат с одним специалистом, форматы размещения, меню программ. Дополнительный сервис для выручки, LTV и привлечения гостей."
|
|
/>
|
|
<meta name="theme-color" content="#0A2540" />
|
|
<meta name="robots" content="index,follow" />
|
|
<link rel="canonical" href="%VITE_SITE_URL%" />
|
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
<link rel="apple-touch-icon" href="/favicon.png" />
|
|
|
|
<meta property="og:type" content="website" />
|
|
<meta property="og:locale" content="ru_RU" />
|
|
<meta property="og:site_name" content="Экзо Групп" />
|
|
<meta property="og:title" content="ЭкзоОтель: пространство восстановления для гостей" />
|
|
<meta
|
|
property="og:description"
|
|
content="Готовое wellness-tech пространство для дополнительной выручки, роста ценности проживания, LTV и привлечения гостей."
|
|
/>
|
|
<meta property="og:url" content="%VITE_SITE_URL%" />
|
|
<meta property="og:image" content="%VITE_SITE_URL%/og-image.jpg" />
|
|
<meta property="og:image:width" content="1200" />
|
|
<meta property="og:image:height" content="630" />
|
|
<meta property="og:image:alt" content="Премиальное пространство ЭкзоОтель с оборудованием Экзо Групп" />
|
|
<meta name="twitter:card" content="summary_large_image" />
|
|
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "Service",
|
|
"name": "ЭкзоОтель — пространство восстановления для гостей отеля",
|
|
"provider": { "@type": "Organization", "name": "Экзо Групп" },
|
|
"serviceType": "Проектирование и запуск кабинета восстановления в отеле",
|
|
"areaServed": "RU",
|
|
"url": "%VITE_SITE_URL%"
|
|
}
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="module" src="/src/main.tsx"></script>
|
|
</body>
|
|
</html>
|