# 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. ## Локальный запуск ```bash 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`: ```json { "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`.