From 494a37ce8f75e9a8813ff62afeb0a7e141b9e659 Mon Sep 17 00:00:00 2001 From: IK Date: Mon, 7 Sep 2026 13:29:13 +0700 Subject: [PATCH] feat: establish public SEO foundation --- README.md | 2 + apps/web/astro.config.mjs | 1 + apps/web/src/layouts/Layout.astro | 39 +++++++++++++++++- apps/web/src/pages/404.astro | 8 ++++ apps/web/src/pages/index.astro | 9 ++++- apps/web/src/pages/privacy.astro | 2 +- apps/web/src/pages/records.astro | 2 +- apps/web/src/pages/report.astro | 2 +- apps/web/src/pages/robots.txt.ts | 16 ++++++++ apps/web/src/pages/rules.astro | 2 +- apps/web/src/pages/sitemap.xml.ts | 19 +++++++++ apps/web/src/pages/spots/[id].astro | 7 +++- apps/web/src/styles/global.css | 1 + docs/ROADMAP.md | 62 +++++++++++++++++++++++++++-- 14 files changed, 160 insertions(+), 12 deletions(-) create mode 100644 apps/web/src/pages/404.astro create mode 100644 apps/web/src/pages/robots.txt.ts create mode 100644 apps/web/src/pages/sitemap.xml.ts diff --git a/README.md b/README.md index e2ffbe7..17471e6 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ RF4DB/RF4-STAT/RF4MAP/RF4 Posts сначала принимаются в изо На сайте у каждой записи отображается источник, а у агрегированной активности — все вошедшие в расчёт источники. Неполные community-наблюдения публикуются сразу в отдельной ленте «Полевые сигналы» с предупреждением и перечнем отсутствующих полей; до подтверждения полноты они не влияют на индекс клёва. +Базовый SEO-контур готов для `rf4spotter.ru`: страницы имеют уникальные метаданные, canonical, Open Graph/Twitter Card и JSON-LD; доступны динамические `/robots.txt` и `/sitemap.xml`, административные и ошибочные страницы закрыты от индексации, добавлена собственная страница 404. Фирменное OG-изображение и индексируемые страницы рыб/водоёмов остаются следующими пакетами в roadmap. + Подробный план и актуальные чекбоксы находятся в [`docs/ROADMAP.md`](docs/ROADMAP.md). Результаты проверки интерфейса и пять приоритетных UX-пакетов описаны в [`docs/UI_UX_AUDIT.md`](docs/UI_UX_AUDIT.md). Production-контур для домена `rf4spotter.ru`, TLS, секреты, backup/restore и команды первого запуска описаны в [`deploy/README.md`](deploy/README.md). Он использует отдельный `compose.production.yaml`; локальный `compose.yaml` остаётся средой разработки. Production seed добавляет только справочники — демонстрационные уловы отключены. Изолированные проверки `deploy/test-production-bootstrap.sh` и `deploy/test-backup-restore.sh` подтверждают старт с пустых volumes и восстановление данных. diff --git a/apps/web/astro.config.mjs b/apps/web/astro.config.mjs index 495c34c..03a0f1d 100644 --- a/apps/web/astro.config.mjs +++ b/apps/web/astro.config.mjs @@ -2,6 +2,7 @@ import { defineConfig } from "astro/config"; import node from "@astrojs/node"; export default defineConfig({ + site: process.env.PUBLIC_SITE_URL || "https://rf4spotter.ru", output: "server", adapter: node({ mode: "standalone" }), server: { host: true, port: 4321 }, diff --git a/apps/web/src/layouts/Layout.astro b/apps/web/src/layouts/Layout.astro index 0e6de31..f033751 100644 --- a/apps/web/src/layouts/Layout.astro +++ b/apps/web/src/layouts/Layout.astro @@ -1,12 +1,47 @@ --- import "../styles/global.css"; import FishingIcon from "../components/FishingIcon.astro"; -const { title = "RF4 Spotter" } = Astro.props; +const { + title = "RF4 Spotter — свежие точки клёва Russian Fishing 4", + description = "Свежие точки, уловы и честная оценка активности в Russian Fishing 4 с обязательным указанием источников.", + noindex = false, + image = "/lake-dawn.png", + structuredData = null, +} = Astro.props; const path = Astro.url.pathname; +const siteUrl = import.meta.env.PUBLIC_SITE_URL || "https://rf4spotter.ru"; +const canonical = new URL(path, siteUrl).toString(); +const socialImage = new URL(image, siteUrl).toString(); +const preventIndexing = noindex || path.startsWith("/admin/"); +const websiteJsonLd = { "@type": "WebSite", name: "RF4 Spotter", url: siteUrl, inLanguage: "ru" }; +const jsonLd = JSON.stringify({ + "@context": "https://schema.org", + "@graph": structuredData ? [websiteJsonLd, structuredData] : [websiteJsonLd], +}).replaceAll("<", "\\u003c"); --- - {title} + + + + + + + + + + + + + + + + + + +