Решил сделать небольшой проект для статей. Для разработки я выбрал Next.js, создал структуру проекта и пошел думать над тем, как мне будет проще и удобнее публиковать и редактировать статьи.
Начал искать подходящую headless CMS. Первое, что выдает поисковик и что у многих на слуху, — это Strapi. Попробовав ее в их тестовой среде, понял, что это мощный инструмент, подходящий для более крупных проектов, где важно уметь управлять всем контентом, однако для небольшого блога с фокусом на статьях Strapi кажется избыточным.
В итоге я продолжил искать и наткнулся на новую, более легковесную CMS — Outstatic, разработанную для управления контентом с использованием Markdown.
О ней и пойдет речь в этой статье.
Outstatic
— это CMS с открытым исходным кодом, которая хранит все ваши данные в виде markdown файлов прямо в проекте. Поэтому она не требует базы данных, не зависит от среды развертывания и не нуждается в отдельной системе контроля версий, что очень удобно.
Быстрый старт в 4 шага
1) GitHub OAuth
Для того, чтобы Outstatic могла автоматически коммитить все изменения в статьях, нужно подключить к ней авторизацию в GitHub.
-
Перешел на «Register a new OAuth app»
-
Application name: имя вашего приложения (app-local)
-
Homepage URL: URL домашней страницы (http://localhost:3000/)
-
Authorization callback URL: <Основной путь>/api/outstatic/callback (http://localhost:3000/api/outstatic/callback)
-
-
После регистрации сгенерировал новый Client secret и записал его в заметки, он понадобится в env переменных
Обратите внимание, что Github не дает прописать сразу несколько путей для аутентификации, поэтому, для приложения развернутого на сервере, понадобится настроить еще один OAuth app.
2) Устанавливаю Outstatic в проект
npm i outstatic
3) Cоздаю два файла в проекте
/app/outstatic/[[…ost]]/page.tsx
import 'outstatic/outstatic.css'; import { Outstatic } from 'outstatic'; import { OstClient } from 'outstatic/client'; export default async function Page({ params }: { params: { ost: string[] } }) { const ostData = await Outstatic(); return <OstClient ostData={ostData} params={params} />; }
/app/api/outstatic/[[…ost]]/route.ts
import { OutstaticApi } from 'outstatic'; export const GET = OutstaticApi.GET; export const POST = OutstaticApi.POST;
4) Остается прописать .env переменные
OST_GITHUB_ID=GITHUB_OAUTH_APP_ID OST_GITHUB_SECRET=GITHUB_OAUTH_APP_SECRETN OST_REPO_SLUG=GITHUB_REPOSITORY_SLUG # OPTIONAL OST_REPO_BRANCH=YOUR_GITHUB_REPOSITORY_BRANCH
Вот и все, теперь можно запускать приложение
Пишем первую статью
Переходим на https://localhost:3000/outstatic и авторизуемся через github.
Можно писать свою первую статью (подробнее как это делать тут)
После сохранения статьи она автоматически попадает в репозиторий, но не обновляет файлы локально, поэтому не забывайте пулить изменения, чтобы подтянулись все обновленные markdown файлы.
Отображение
Теперь стоит задача отобразить список постов и конкретный пост по его id.
Для начала давайте напишем функцию, которая трансформирует наш markdown в html, чтобы отобразить его на странице. Для этого будем использовать unified и remark-parse
Установим все необходимые зависимости
npm i unified remark-parse remark-gfm remark-rehype rehype-sanitize rehype-stringify
Создадим файл
lib/markdownToHtml.ts
import { unified } from 'unified'; import remarkParse from 'remark-parse'; import remarkGfm from 'remark-gfm'; import remarkRehype from 'remark-rehype'; import rehypeSanitize from 'rehype-sanitize'; import rehypeStringify from 'rehype-stringify'; export default async function markdownToHtml(markdown: string) { const result = await unified() .use(remarkParse) .use(remarkGfm) .use(remarkRehype) .use(rehypeSanitize) .use(rehypeStringify) .process(markdown); return result.toString(); }
Получение данных
Напишем функцию, которая получит отсортированный список постов по категории, и функцию для получения контента поста. Поле категории я добавил при создании поста в Outstatic (тут об этом подробно написано)
/lib/posts.ts
import { getDocuments, getDocumentBySlug } from 'outstatic/server'; import markdownToHtml from './markdownToHtml'; export function getSortedPostsData({ category }: { category: string; }) { const allPostsData = getDocuments(category, [ 'title', 'status', 'author', 'slug', 'description', 'coverImage', 'publishedAt', 'price', 'category', 'tags', ]); return allPostsData.sort((a, b) => { if (a.publishedAt < b.publishedAt) { return 1; } else { return -1; } }); } export async function getPostContent({ category, slug }: { category: string; slug: string }) { const post = getDocumentBySlug(category, slug, [ 'title', 'status', 'author', 'slug', 'description', 'coverImage', 'publishedAt', 'content', 'category', ]); if (!post) return null; const content = await markdownToHtml(post.content); return { ...post, content, }; }
Также Outstatic поддерживает простые запросы на выборку данных (подробнее тут)
Создадим директорию в /app, в которой будут отображаться все наши посты.
/app/posts/page.tsx
import { getSortedPostsData } from '@/lib/posts'; import Image from 'next/image'; import Link from 'next/link'; export default async function Page() { const allPostsData = getSortedPostsData({ category: 'tilda-widgets', }); return ( <div> {allPostsData.map((post) => ( <Link key={post.slug} href={`/${post.category}/${post.slug}`}> <Image width={200} height={200} src={post.coverImage} alt={post.title} /> </Link> ))} </div> ); }
/app/posts/[id]/page.tsx
import { getPostContent } from '@/lib/posts'; import Image from 'next/image'; export default async function Page({ params }: { params: { id: string } }) { const postInfo = await getPostContent({ category: 'tilda-widgets', slug: params.id }); if (!postInfo) return <p>Такого поста нет</p>; return ( <div> <h1>{postInfo.title}</h1> <Image src={postInfo.coverImage} alt={postInfo.title} width={0} height={0} sizes='100vw' /> <div dangerouslySetInnerHTML={{ __html: postInfo.content ?? '' }}></div> </div> ); }
После перезапуска приложения, мы должны увидеть список постов, и при переходе по элементам сам контент поста.
Обратите внимание, что стиль в редакторе и в самом посте будут отличаться, поэтому нужно поработать со стилями. Для отображения блока с кодом можете использовать prismjs.
Вот и все, минимальный функционал для нашего блога готов.
Минусы
После использования Outstatic из минусов могу выделить следующее:
-
Невозможно прикрепить несколько изображений к заголовку
-
Нет автосохранения контента и в целом не всегда уверен, что изменения улетели в гит, поэтому приходится перепроверять перед закрытием
-
Нет возможности редактировать сам markdown напрямую из редактора
Но несмотря на все эти минусы, считаю, что это очень легковесный и удобный продукт для небольших блогов.
Спасибо за внимание
ссылка на оригинал статьи https://habr.com/ru/articles/857278/
Добавить комментарий