rewrite: Vite+React → Astro 5 + Content Collections
Some checks failed
deploy / deploy (push) Failing after 12s

- Бэкап старой версии на ветке vite-react-backup
- Stack: Astro 5 + nginx:alpine runtime, образ ~50 МБ (был ~600 МБ)
- @astrojs/rss заменён ручным buildRss() — гарантия CDATA в content:encoded для IPB Importer
- @astrojs/sitemap → sitemap-index.xml + sitemap.txt
- 152-ФЗ cookie consent + privacy.astro + Analytics с gating
- AI-файлы: robots.txt с явным allow для AI-краулеров, ai.txt, llms.txt
- Гибридный визуал: фото-фон шапки (аэрофото Пушкино) + PT Serif + IBM Plex Sans
- Иерархия: hero "Главная история" с рамкой + "Ещё из истории" + "Хроника"
- Категория "main" (псевдо) скрыта из плашек и из Рубрик в сайдбаре
- hideFromList: true для технических постов
- featuredImage в frontmatter для постов без хорошей первой <img>
- WP resized-URL (-WxH.ext) автоматически → оригинал
- CI/CD: .gitea/workflows/deploy.yml (push → SSH-build)
- Внешние RSS: scripts/pull-external-rss.mjs пишет news.json в bind-mount, фронт фетчит client-side
This commit is contained in:
striker
2026-05-21 03:21:31 +03:00
parent a0219ee8f3
commit c65e07cd98
75 changed files with 5926 additions and 4142 deletions

View File

@@ -0,0 +1,49 @@
---
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import PostCard from '../../components/PostCard.astro';
import { plural } from '../../consts';
export async function getStaticPaths() {
const all = await getCollection('posts');
const slugs = new Map<string, string>(); // slug -> display name
for (const p of all) {
p.data.categorySlugs.forEach((s, i) => {
if (!slugs.has(s)) slugs.set(s, p.data.categories[i] ?? s);
});
}
return [...slugs.entries()].map(([slug, name]) => ({
params: { slug },
props: { catSlug: slug, catName: name },
}));
}
const { catSlug, catName } = Astro.props;
const all = await getCollection('posts');
const posts = all
.filter((p) => p.data.categorySlugs.includes(catSlug))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
---
<BaseLayout title={`Категория: ${catName}`} description={`Записи в рубрике «${catName}»`}>
<div class="cat-head">
<h1>Категория: {catName}</h1>
<p class="meta">
{posts.length}&nbsp;{plural(posts.length, ['запись', 'записи', 'записей'])}
&middot;
<a href={`/cat/${catSlug}/feed.xml`}>RSS этой рубрики</a>
</p>
</div>
<div class="post-list">
{posts.map((p) => <PostCard post={p} />)}
{posts.length === 0 && <p class="empty">В этой рубрике пока нет записей.</p>}
</div>
</BaseLayout>
<style>
.cat-head { border-bottom: 1px solid var(--rule); padding-bottom: 1rem; margin-bottom: 1rem; }
.cat-head h1 { font-family: var(--font-serif); font-size: 1.8rem; margin: 0; }
.cat-head .meta { margin: 0.5rem 0 0; font-size: 0.85rem; color: var(--muted); }
.cat-head a { color: var(--accent); }
.empty { color: var(--muted); margin: 1rem 0; }
</style>