From 7f85f34b6a517c2a84697c3b6c3cca0259a1d4cb Mon Sep 17 00:00:00 2001 From: striker Date: Thu, 21 May 2026 02:47:39 +0300 Subject: [PATCH] =?UTF-8?q?docs(CLAUDE.md)=20+=20fix(nginx):=20application?= =?UTF-8?q?/rss+xml=20=D0=B4=D0=BB=D1=8F=20feed.xml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CLAUDE.md проекта: сервер, cutover-инструкция, откат за минуту, бэкап-пути, стек, структура, RSS-конфигурация под IPB, аналитика+consent, CI/CD, локальная разработка - nginx.conf: types{} default_type application/rss+xml — теперь Content-Type правильный для RSS Importer (раньше отдавалось text/xml из дефолтного MIME) --- CLAUDE.md | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++ nginx.conf | 6 +- 2 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..efcc665 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,162 @@ +# anotherreflections.ru — v2 (Astro) + +## Проект + +Главный сайт-портал ролевой группы «Иные Отражения». Переделан со старого WordPress 6.9 (тема `darkness-10`, 23 активных плагина) на статический **Astro 5 + Content Collections + markdown**. + +## Сервер + +| Параметр | Значение | +|---|---| +| Хост | `web.hhivp.com` (45.10.53.206 / 45.10.53.242) | +| Deploy path | `/opt/docker/sites/anotherreflections-ru-v2/` (git clone) | +| Git remote | `git.striker.su/striker/anotherreflections-website-v2` (ветка `main`) | +| Контейнер | `anotherreflections-ru-v2` (nginx:1.29-alpine) | +| Внутренний порт | 80 | +| Внешний порт | 127.0.0.1:**4084** (nginx-хост → proxy_pass) | +| nginx vhost | `/etc/nginx/conf.d/anotherreflections.ru` | +| OG-image / лого | `public/og-image.png`, `public/logo*.svg` | + +## Cutover (2026-05-20) + +Со старого WordPress (контейнер `anotherreflections-ru` на :4080) — на новый Astro-контейнер (:4084). Старый контейнер и БД оставлены для отката на 1-2 недели. + +**Откат за 1 минуту:** +```bash +ssh striker@web.hhivp.com +sudo sed -i 's|127.0.0.1:4084|127.0.0.1:4080|g' /etc/nginx/conf.d/anotherreflections.ru +sudo nginx -t && sudo systemctl reload nginx +``` + +**Бэкап перед cutover** в `/opt/backup/anotherreflections-pre-v2-cutover-20260521-024027/`: +- `wp-site-tree.tgz` (349 МБ) — весь `/opt/docker/sites/anotherreflections-ru/` +- `wp-db-anotherreflctions_ru.sql.gz` (5.6 МБ) — mysqldump БД (на `db.hhivp.com` MySQL) +- `anotherreflections.ru.nginx` — старый vhost (proxy 4080) + +Также резерв-копия vhost в `/opt/backup/nginx-bak/20260521-024433/anotherreflections.ru.bak.20260520-wp`. + +## Стек + +- **Astro 5** (6.3.6) + Content Collections — каждая `.astro` страница рендерится в `dist/.../index.html` при `npm run build` (SSG, без runtime JS-фреймворка). +- **@astrojs/rss** — RSS-фиды (общий + per-category) под IPB RSS Importer. +- **@astrojs/sitemap** — `sitemap-index.xml` автоматически. +- **sanitize-html** — очистка HTML тела поста для `` в RSS. +- **sharp** (devDep) — генерация OG-image PNG из SVG. + +## Структура + +``` +src/ +├── content/ +│ ├── posts/*.md (50 постов 2009-2015, мигрированы из WP) +│ ├── pages/*.md (6 страниц: O нас, Наши друзья, страницы про игры) +│ └── _categories.json (10 категорий-справочник) +├── components/ +│ ├── BrandMark.astro (SVG-знак в шапке) +│ ├── SocialLinks.astro (плавающая правая колонка: Telegram, ВК, ↑) +│ ├── Analytics.astro (Яндекс.Метрика + GA, скрипты с type=text/plain +│ │ активируются после consent) +│ └── CookieConsent.astro (152-ФЗ-баннер, ar-consent в localStorage+cookie) +├── layouts/BaseLayout.astro +├── pages/ +│ ├── index.astro (hero + лента всех постов) +│ ├── [slug].astro (один пост или одна страница, slug совпадает с WP) +│ ├── 404.astro +│ ├── miry.astro (8 игровых вселенных карточками) +│ ├── kontakty.astro (email + Telegram + ВК) +│ ├── privacy.astro (политика + кнопка «Отозвать согласие») +│ ├── feed.xml.ts (общий RSS, фильтр по RSS_CUTOFF) +│ ├── sitemap.txt.ts (plain-text список всех URL) +│ └── category/ +│ ├── [slug].astro +│ └── [slug]/feed.xml.ts (per-category RSS) +├── lib/rss-helpers.ts (рендер md→HTML для ) +├── styles/global.css (тёмная тема, decorative ::before/::after звёзды) +└── consts.ts (SITE_TITLE, WORLDS, ANALYTICS, CATEGORY_COLORS, plural()) + +public/ +├── favicon.svg +├── logo.svg, logo-mark.svg +├── og-image.png (1200×630 для расшаривания в TG/VK/Twitter) +├── og-image.svg (исходник) +├── robots.txt (allow all, Host для Yandex, AI-crawlers) +├── ai.txt (Train/Cite/Quote: yes) +├── llms.txt (формат llmstxt.org) +└── wp-content/uploads/ (4 файла, перенесены со старого WP: header_bg + NY-baner) + +scripts/ +├── migrate-wp.mjs (одноразовый: _wp-export.json → src/content/*.md) +└── build-og-image.mjs (sharp → public/og-image.png из SVG) + +Dockerfile (multi-stage: node:22-alpine builds → nginx:1.29-alpine serves) +nginx.conf (gzip, кэш _astro/ 1y, MIME application/rss+xml для feed) +docker-compose.yml (контейнер на 127.0.0.1:4084) +.gitea/workflows/deploy.yml (push в main → SSH-деплой на web) +``` + +## RSS под IPB Importer + +`RSS_CUTOFF` в `src/consts.ts` (по умолчанию `2026-05-20`) — фид отдаёт **только посты с pubDate ≥ cutoff**. Архив 2009-2015 на сайте остаётся, в форумы через RSS Importer не вбрасывается. Чтобы добавить пост в фид — публикуем с датой ≥ cutoff и пересобираем. + +RSS 2.0 strict: `` в RFC-822, `` = URL поста (IPB дедуплицирует), `` с CDATA + полным HTML тела, `` обновляется при каждом build. + +URLs: +- `/feed.xml` — общий +- `/category//feed.xml` — по категории + +## Аналитика и cookie consent + +- **Яндекс.Метрика** 13938862 (с webvisor) и **Google gtag** GT-PH39R3X. ID-шки в `src/consts.ts:ANALYTICS`. +- Оба скрипта в `Analytics.astro` имеют `type="text/plain" data-cookieconsent="statistics"` — браузер их **не выполняет**, пока пользователь не нажмёт «Принять» в баннере. +- `CookieConsent.astro` активирует скрипты при согласии, сохраняет выбор в `localStorage` + `cookie ar-consent=accept|deny` (12 мес). +- На `/privacy/` кнопка «Отозвать согласие» — ставит `ar-consent=deny` одним кликом. + +## Деплой + +**Через CI/CD (Gitea Actions)** — push в `main`: +1. Workflow `.gitea/workflows/deploy.yml` стартует. +2. SSH на `web.hhivp.com` с ключом из секрета `SSH_PRIVATE_KEY` (base64). +3. `git fetch` + `git reset --hard origin/main` → `docker compose build --pull` → `docker compose up -d` → `docker image prune -af --filter "until=168h"`. +4. Verify-шаг: `curl -sf -H "Host: anotherreflections.ru" http://127.0.0.1:4084/`. + +Секреты в Gitea (`/repos/striker/anotherreflections-website-v2/actions/secrets`): `SSH_PRIVATE_KEY`, `SSH_HOST=web.hhivp.com`, `SSH_USER=striker`, `SSH_PORT=22`. Deploy-ключ — `~/.ssh/anotherreflections-v2-deploy{,.pub}` локально, `.pub` в `~striker/.ssh/authorized_keys` на web. + +**Вручную** (с локалки): +```bash +cd /opt/docker/sites/anotherreflections-ru-v2 +git pull +docker compose build && docker compose up -d +``` + +## База данных + +В новой версии БД нет. Старый WP оставлен с БД `anotherreflctions_ru` (`sic`, с опечаткой) на `db.hhivp.com` (45.10.53.205, MySQL), user `u_anotherreflections`, prefix `anm_`. После 1-2 недель наблюдения за новой версией — старый WP-контейнер можно удалить, БД и snapshot тоже. + +## SEO/AI файлы + +- `public/robots.txt` — статика, разрешено всё; явно перечислены GPTBot/ClaudeBot/Google-Extended/CCBot/PerplexityBot/anthropic-ai; ссылки на sitemap-index.xml и sitemap.txt +- `public/ai.txt` — Train/Cite/Quote: yes (по спецификации spawning.ai) +- `public/llms.txt` — формат llmstxt.org с описанием проекта, ключевыми страницами, форумами, RSS-фидами, контактами +- `src/pages/sitemap.txt.ts` — генерирует plain-text список 68 URL при каждом билде +- `@astrojs/sitemap` — `sitemap-index.xml` + `sitemap-0.xml` XML-формат + +## Скриншоты-источники + +В `E:/Projects/` лежат финальные: +- `anotherreflections-logo.svg` / `.png` — знак + надпись +- `anotherreflections-logo-mark.svg` / `.png` — только знак + +## Локальная разработка + +```bash +npm install +npm run dev # → http://localhost:4321 +npm run build # → dist/ (статика) +npm run preview # просмотр build +npm run migrate # одноразовая миграция _wp-export.json → md (уже сделана) +``` + +## История + +- **2026-05-20**: v1 (WordPress 6.9 + darkness-10 + 23 плагина, контейнер на :4080) переделан на Astro 5. Новый репо `git.striker.su/striker/anotherreflections-website-v2`. Cutover в production через nginx proxy_pass swap. См. также `memory/project_anotherreflections_main_site.md`. +- **2026-05-07**: 6 IPB-форумов проекта мигрированы со str-u-01 на web.hhivp.com (порты 4091-4096). См. `memory/project_anotherreflections_forums.md`. diff --git a/nginx.conf b/nginx.conf index e120074..5bdd4cd 100644 --- a/nginx.conf +++ b/nginx.conf @@ -36,15 +36,17 @@ server { # ──────────────────────────────────────────────────────────────── # MIME для RSS-фидов и текстовых служебных файлов # ──────────────────────────────────────────────────────────────── + # types{} перебивает дефолтный mime, чтобы Content-Type был application/rss+xml, + # а не text/xml (так RSS Importers в IPB точно поймут как RSS-feed). location = /feed.xml { - default_type application/rss+xml; + types { } default_type application/rss+xml; charset utf-8; try_files /feed.xml =404; expires 5m; add_header Cache-Control "public, max-age=300, must-revalidate"; } location ~ ^/category/[^/]+/feed\.xml$ { - default_type application/rss+xml; + types { } default_type application/rss+xml; charset utf-8; expires 5m; add_header Cache-Control "public, max-age=300, must-revalidate";