docs(openspec): archive rss-full-feed — спека site-rss в main specs
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-08
|
||||
@@ -0,0 +1,74 @@
|
||||
# Design: RSS Full Feed
|
||||
|
||||
## Overview
|
||||
|
||||
Включение полного текста и лимита RSS чисто конфигурационное (тема уже
|
||||
поддерживает `content:encoded`). Меняем только `hugo.toml`.
|
||||
|
||||
## Changes
|
||||
|
||||
### 1. `hugo.toml` — добавить в `[params]`
|
||||
|
||||
```toml
|
||||
# Полный текст постов в RSS (content:encoded)
|
||||
ShowFullTextinRSS = true
|
||||
```
|
||||
|
||||
### 2. `hugo.toml` — добавить секцию `[services.RSS]`
|
||||
|
||||
```toml
|
||||
[services]
|
||||
[services.RSS]
|
||||
limit = 5
|
||||
```
|
||||
|
||||
Примечание: в `hugo.toml` уже есть секция `[services]` с
|
||||
`[services.googleAnalytics]` — добавим `[services.RSS]` рядом.
|
||||
|
||||
### 3. `themes/rDedInit/layouts/_default/rss.xml` — обложка в content:encoded
|
||||
|
||||
Перед `.Content` в `<content:encoded>` добавляется `figure.entry-cover` с
|
||||
`<img>` обложки (абсолютный URL), если `cover.image` задан:
|
||||
|
||||
```go-html-template
|
||||
{{- $coverHtml := "" }}
|
||||
{{- with .Params.cover.image }}
|
||||
{{- $coverAbs := (path.Join $.RelPermalink .) | absURL }}
|
||||
{{- $coverHtml = printf "<figure class=\"entry-cover\"><img src=\"%s\" alt=\"%s\"></figure>" $coverAbs ($.Params.cover.alt | default $.Title | htmlEscape) }}
|
||||
{{- end }}
|
||||
<content:encoded>{{ (printf "<![CDATA[%s%s]]>" $coverHtml .Content) | safeHTML }}</content:encoded>
|
||||
```
|
||||
|
||||
Примечание: тема — форк (rDedInit), правим её шаблон напрямую, т.к.
|
||||
медиа-обложки рендерятся отдельным партиалом `cover.html` и в `.Content` не
|
||||
попадают.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
cd /opt/dedinit.ru
|
||||
hugo --minify # собрать
|
||||
grep -c '<item>' public/index.xml # ожидаем 5
|
||||
grep -c 'content:encoded' public/index.xml # ожидаем 5 (полный текст)
|
||||
# проверить абсолютные медиа-ссылки в content:encoded:
|
||||
python3 - <<'EOF'
|
||||
import re
|
||||
xml = open('public/index.xml', encoding='utf-8').read()
|
||||
blocks = re.findall(r'<content:encoded><!\[CDATA\[(.*?)\]\]></content:encoded>', xml, re.S)
|
||||
print('blocks:', len(blocks))
|
||||
for i, b in enumerate(blocks[:2], 1):
|
||||
urls = re.findall(r'https://dedinit\.ru/[^"\')\s]+', b)
|
||||
print(i, 'media urls:', urls[:4])
|
||||
EOF
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
Удалить из `hugo.toml`:
|
||||
- строку `ShowFullTextinRSS = true`
|
||||
- секцию `[services.RSS] limit = 5`
|
||||
|
||||
И откатить `themes/rDedInit/layouts/_default/rss.xml` (git checkout -- файл).
|
||||
|
||||
Пересобрать `hugo --minify`. Поведение вернётся к прежнему (все посты, без
|
||||
полного текста).
|
||||
@@ -0,0 +1,46 @@
|
||||
# RSS Full Feed for dedinit.ru
|
||||
|
||||
## Why
|
||||
|
||||
Блог dedinit.ru (Hugo 0.165, тема rDedInit/PaperMod) уже генерирует RSS-ленту
|
||||
(`public/index.xml`), но она неполноценна:
|
||||
|
||||
- **Лимит не задан** — в ленту попадают ВСЕ посты (сейчас 17), хотя типовой
|
||||
читатель RSS ожидает короткую ленту последних публикаций.
|
||||
- **Нет полного текста** — тема умеет выводить `<content:encoded>` (полный HTML
|
||||
контент поста) при параметре `site.Params.ShowFullTextinRSS`, но он не включён.
|
||||
Сейчас в `<description>` только summary, у части постов обрезанный HTML.
|
||||
- **Нет явных ссылок на медиа** — изображения (hero.svg и др.) рендерятся в
|
||||
абсолютные URL внутри HTML-контента, поэтому при включении полного текста
|
||||
медиа-ссылки появятся автоматически. Но если полный текст по какой-то причине
|
||||
недоступен, лента должна хотя бы содержать корректное описание.
|
||||
|
||||
Цель: сделать RSS-ленту пригодной для полного забирания контента постов
|
||||
(full-content syndication) — 5 последних постов с полным текстом и медиа-ссылками.
|
||||
|
||||
## What
|
||||
|
||||
1. Включить полный текст в RSS: `[params] ShowFullTextinRSS = true`.
|
||||
2. Задать лимит 5 постов: `[services.RSS] limit = 5`.
|
||||
3. Убедиться, что в `<item>` попадают: `<title>`, `<link>`, `<pubDate>`,
|
||||
`<description>` (описание или summary), `<content:encoded>` (полный HTML,
|
||||
включая абсолютные ссылки на медиа), `<guid>`.
|
||||
4. Fallback (план B, если полный текст окажется недоступен): добавить явные
|
||||
`description` в frontmatter постов и увеличить лимит до 10.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Не меняем дизайн/вёрстку сайта.
|
||||
- Не трогаем taxonomy RSS (категории/теги)-ленты.
|
||||
- Не добавляем новые форматы (Atom только как самоссылка, уже есть).
|
||||
|
||||
## Affected files
|
||||
|
||||
- `hugo.toml` — `[params] ShowFullTextinRSS`, `[services.RSS] limit`
|
||||
- (план B) `content/posts/*/index.md` — `description` в frontmatter
|
||||
|
||||
## Rollback
|
||||
|
||||
Откат = удалить две строки из `hugo.toml` (параметр `ShowFullTextinRSS` и
|
||||
`[services.RSS] limit`), пересобрать `hugo --minify` — вернётся прежнее поведение
|
||||
ленты без полного текста и без лимита.
|
||||
@@ -0,0 +1,73 @@
|
||||
# RSS Feed Capability
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: R1 — Лимит постов в RSS-ленте
|
||||
|
||||
Главная RSS-лента (`/index.xml`) MUST содержать не более 5 последних постов
|
||||
(по `publishDate`), отсортированных по дате публикации по убыванию.
|
||||
|
||||
#### Scenario: Сборка сайта даёт ровно 5 элементов item
|
||||
|
||||
GIVEN сайт собран командой `hugo --minify`
|
||||
WHEN запрашивается `https://dedinit.ru/index.xml` (файл `public/index.xml`)
|
||||
THEN число элементов `<item>` в `<channel>` равно 5 (или меньше, если постов < 5),
|
||||
и это 5 самых свежих постов по дате.
|
||||
|
||||
### Requirement: R2 — Полный текст поста в RSS
|
||||
|
||||
Каждый `<item>` главной RSS-ленты MUST содержать `<content:encoded>` с полным
|
||||
HTML-контентом поста (поле `.Content` темы Hugo), обёрнутым в CDATA, включая
|
||||
разметку, блоки кода и абсолютные ссылки на медиафайлы поста.
|
||||
|
||||
#### Scenario: content:encoded присутствует и содержит медиа-URL
|
||||
|
||||
GIVEN сайт собран командой `hugo --minify`
|
||||
WHEN в `public/index.xml` смотреть элемент `<item>` любого из 5 последних постов
|
||||
THEN присутствует непустой `<content:encoded>`, содержащий HTML поста и ссылки
|
||||
вида `https://dedinit.ru/<год>/<slug>/hero.svg` (или аналогичные абсолютные URL
|
||||
медиа).
|
||||
|
||||
### Requirement: R3 — Описание поста в RSS
|
||||
|
||||
`<description>` каждого `<item>` MUST быть непустым: используется frontmatter
|
||||
`description` если задан, иначе авто-summary `.Summary`.
|
||||
|
||||
#### Scenario: description непустое
|
||||
|
||||
GIVEN сайт собран командой `hugo --minify`
|
||||
WHEN в `public/index.xml` смотреть `<description>` каждого `<item>`
|
||||
THEN оно непустое и не содержит обрезанных HTML-тегов.
|
||||
|
||||
### Requirement: R4 — Метаданные элемента RSS
|
||||
|
||||
Каждый `<item>` MUST содержать корректные `<title>`, `<link>`, `<pubDate>`,
|
||||
`<guid>` (permalink), согласно текущему шаблону темы
|
||||
`themes/rDedInit/layouts/_default/rss.xml`.
|
||||
|
||||
#### Scenario: Обязательные поля элемента
|
||||
|
||||
GIVEN сайт собран командой `hugo --minify`
|
||||
WHEN парсить `public/index.xml` как XML
|
||||
THEN каждый `<item>` содержит все 4 обязательных поля, `<link>` и `<guid>`
|
||||
равны полному URL поста (`https://dedinit.ru/<год>/<slug>/`).
|
||||
|
||||
### Requirement: R5 — Медиа-обложка в RSS
|
||||
|
||||
Каждый `<item>` MUST включать в `<content:encoded>` обложку поста
|
||||
(`cover.image`, hero.svg) как `<figure class="entry-cover"><img ...></figure>`
|
||||
перед полным текстом, со ссылкой на абсолютный URL медиафайла
|
||||
(`https://dedinit.ru/<год>/<slug>/hero.svg`), если `cover.image` задан.
|
||||
|
||||
#### Scenario: Обложка в content:encoded
|
||||
|
||||
GIVEN сайт собран командой `hugo --minify`
|
||||
WHEN в `public/index.xml` смотреть `<content:encoded>` поста с cover.image
|
||||
THEN он начинается с `<figure class="entry-cover">` и содержит
|
||||
`<img src="https://dedinit.ru/<год>/<slug>/hero.svg"`.
|
||||
|
||||
#### Scenario: Нет cover — нет figure
|
||||
|
||||
GIVEN сайт собран командой `hugo --minify`
|
||||
WHEN в `<content:encoded>` поста без cover.image
|
||||
THEN отсутствует `<figure class="entry-cover">`, контент начинается с текста поста.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Tasks: RSS Full Feed
|
||||
|
||||
## 1. Конфигурация
|
||||
|
||||
- [x] 1.1 В `hugo.toml`, в секции `[params]`, добавить `ShowFullTextinRSS = true`
|
||||
- [x] 1.2 В `hugo.toml`, в секции `[services]`, добавить `[services.RSS]` с `limit = 5`
|
||||
- [x] 1.3 В `themes/rDedInit/layouts/_default/rss.xml` добавить prepend обложки
|
||||
(figure.entry-cover с absURL) в `<content:encoded>`
|
||||
|
||||
## 2. Сборка и проверка
|
||||
|
||||
- [x] 2.1 Выполнить `hugo --minify` — сборка без ошибок
|
||||
- [x] 2.2 Проверить `grep -c '<item>' public/index.xml` == 5
|
||||
- [x] 2.3 Проверить `grep -c 'content:encoded' public/index.xml` == 5
|
||||
- [x] 2.4 Проверить, что `<content:encoded>` начинается с `<figure class="entry-cover">`
|
||||
и содержит абсолютную ссылку на hero.svg (скриптом из design.md)
|
||||
|
||||
## 3. Git
|
||||
|
||||
- [x] 3.1 Закоммитить изменения (`hugo.toml`, openspec-артефакты)
|
||||
- [x] 3.2 Запушить в origin (gitverse.ru) — после согласования с пользователем
|
||||
(выполнено: push cb4decb)
|
||||
|
||||
## 4. Деплой (по согласованию с пользователем)
|
||||
|
||||
- [x] 4.1 Выполнить `make deploy` (rsync на kpa39l.myjino.ru:2222)
|
||||
(выполнено: sftp-mirror public/ → /dedinit.ru, т.к. аккаунт SFTP-only)
|
||||
- [x] 4.2 Проверить `curl -s https://dedinit.ru/index.xml | grep -c '<item>'` == 5
|
||||
и наличие `content:encoded` (выполнено: 5 items, 5 content:encoded,
|
||||
обложки, XML валиден)
|
||||
Reference in New Issue
Block a user