Files

Forma N8 Site

Отдельное SSR-приложение публичного сайта Forma N8. Контент и постоянные URL не зависят от будущего дизайна.

Возможности

  • лента новостей, страницы статей, категорий и производителей;
  • PostgreSQL со стабильными external_id и slug;
  • idempotent API публикации и отдельный upload API;
  • RSS 2.0, sitemap, robots.txt;
  • canonical, Open Graph и JSON-LD NewsArticle;
  • системный деплой через nginx + systemd.

Локальный запуск

python -m venv .venv
.venv/bin/pip install -r requirements.txt
export DATABASE_URL=postgresql://forma_n8:forma_n8@127.0.0.1:5432/forma_n8
PYTHONPATH=src .venv/bin/python scripts/migrate.py
PYTHONPATH=src .venv/bin/uvicorn forma_n8_site.app:app --port 8090

Публикационный API

Сначала медиа загружается через POST /api/v1/media с заголовком X-Forma-Token, затем полученный относительный URL передаётся в статью.

POST /api/v1/articles:

{
  "external_id": "parser:518",
  "title": "Заголовок новости",
  "lead": "Краткое описание",
  "body": "Основной текст без HTML",
  "category_name": "Снаряжение",
  "category_tag": "snaryazhenie",
  "producer_name": "Производитель",
  "producer_tag": "producer",
  "source_url": "https://example.com/source",
  "status": "published",
  "published_at": "2026-06-21T10:00:00+05:00",
  "media": [
    {"type": "image", "url": "/media/ab/hash.jpg", "alt": "Описание изображения"}
  ]
}

Повторный запрос с тем же external_id обновляет материал, но сохраняет первоначальные slug и дату первой публикации. Это предотвращает дубли URL и потерю поисковой истории.

Публичное название категории берётся из таблицы categories по нормализованному category_tag. Описательные названия категорий из AI-контура не используются в URL, H1 и SEO-метаданных.

После подключения домена меняется только SITE_BASE_URL в /etc/forma-n8.env; затем настраивается TLS и отправляются sitemap в Google Search Console и Яндекс Вебмастер.

До подключения домена SITE_INDEXING_ENABLED=false: страницы получают noindex, а robots.txt запрещает обход IP-адреса. После настройки домена, HTTPS и 301-редиректа с IP значение меняется на true.