Использование GitHub в качестве CMS

После перезапуска моего сайта с совершенно новым дизайном я добавил ряд интересных функций. Одной из таких функций, о которой стоит рассказать, стала страница моего портфолио, поскольку она использует GitHub в качестве CMS (системы управления контентом).

Итак, давайте разберемся, почему я добавил эту функцию, почему не использовать другие сервисы и, конечно же, как она работает.

❓ Почему

При создании нового дизайна своего сайта я ставил перед собой ряд целей. Одной из таких целей было сохранить страницу портфолио, похожую на страницу моего существующего на тот момент веб-сайта, которая получала все мои репозитории GitHub и просто перечисляла их.

Однако я хотел усовершенствовать эту страницу, добавив к ней что-то вроде кураторства. Предпочтительно с помощью какого-то быстрого, простого в использовании метода, доступного практически отовсюду и желательно бесплатного.

☁️ Безголовая CMS

Я знал, что мне нужна какая-то безголовая CMS в той или иной форме, чтобы позволить мне курировать контент, который я хочу отображать, поэтому я немного подумал и провел исследование.

Я обнаружил, что большинство людей с радостью рекомендовали другие сервисы безголовых CMS, такие как Strapi, Sanity, GraphCMS и т.д., которые, казалось, действительно выполняли нужную мне задачу — предоставляли платформу для курирования и управления контентом без необходимости его развертывания. Но у большинства из них были те же проблемы, которые мне не нравились.

Устойчивость

Является ли излишеством пытаться & чтобы ваш личный сайт-портфолио имел 99,999% времени безотказной работы? Возможно.

Имеет ли значение, если мой сайт упадет? Нет. Но мне нравится стараться & относиться как к любой другой производственной среде & по возможности не добавлять еще одну возможную точку отказа. В данном случае это сторонний сервис, обслуживающий данные для моей страницы портфолио.

Если сторонний сервис выйдет из строя, будет ли мое содержимое каким-то волшебным образом обслуживаться в кэше до тех пор, пока сервис не восстановится? Или же контент не будет обслуживаться вообще?

«Но вы ведь берете данные с GitHub, верно? Значит, вы все еще полагаетесь на данные от них» Справедливое замечание, да, это так. Однако я с большей вероятностью доверяю времени работы GitHub и устойчивости платформы, чем сервису CMS, и мне проще добавить систему защиты от сбоев, чтобы в случае сбоя GitHub мы могли обслуживать контент без них (подробнее об этом позже).

Цена

Некоторые могут назвать меня дешевкой за это, но еще одна цель моего личного сайта — быть как можно дешевле. На данный момент мой сайт обходится мне в $20 в год. Вот и все.

Как? Просто. 20 долларов в год за домен, код размещен на GitHub & развернут на Vercel. Это так дешево, и я хочу, чтобы так было и дальше.

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

🔨 Как

Если вы предпочитаете просто посмотреть на окончательный код, чтобы увидеть, как он работает, проверьте файл projects.ts в репозитории GitHub моих сайтов.

Вкратце это работает следующим образом: Используя темы на GitHub, вы можете фильтровать содержимое, которое вы хотите & не хотите показывать. В моем случае я присвоил всем выбранным мной хранилищам тему portfolio & это заставит хранилище отображаться на странице портфолио.

Чтобы заставить это работать, нам понадобятся 2 ключевые вещи: Next.js & GitHub api. По желанию вы также можете использовать какой-нибудь кэш, например Upstash, чтобы кэшировать данные и экономить на запросах к GitHub api.

Получение репозиториев

Для начала мы настроим базовую функцию getServerSideProps, которая позволит нам получить данные репозитория на сервере и передать их клиенту.

// portfolio.jsx
export async function getServerSideProps () {
    const response = await fetch('https://api.github.com/users/GITHUB_USERNAME/repos');
    const json = await response.json();
    return {
        props: {},
    };
};
Вход в полноэкранный режим Выход из полноэкранного режима

Фильтрация репозитория

Здесь мы можем добавить свою собственную логику для обработки данных на основе данных, полученных от GitHub. В этом примере мы будем отфильтровывать все репозитории, в которых отсутствует тема portfolio и которые не архивированы.

// portfolio.jsx
export async function getServerSideProps () {
    // ...
    const repositories = json.map((repo) => {
        if (!repo.topics.includes('portfolio')) return null;
        if (repo.archived) return null;
        return repo;
    })
    .filter((project) => project !== null);
    return {
        props: {
            repositories,
        }
    }
};
Вход в полноэкранный режим Выход из полноэкранного режима

Аутентификация на GitHub

Далее следует необязательный шаг, который я настоятельно рекомендую выполнить, чтобы снизить вероятность превышения лимита скорости GitHub api.

Для выполнения этого шага вам сначала потребуется создать персональный токен доступа GitHub. Вы можете следовать официальному руководству здесь.

Со сгенерированным токеном доступа добавьте его в файл .env.

GITHUB_PAT=abc123
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Добавив его, вы можете обновить запрос на выборку, чтобы добавить токен в заголовки запроса.

// portfolio.jsx
export async function getServerSideProps () {
    const response = await fetch('https://api.github.com/users/GITHUB_USERNAME/repos', {
        headers: {
            authorization: `token ${process.env.GITHUB_PAT}`,
        },
    });
    // ...
};
Вход в полноэкранный режим Выход из полноэкранного режима

Кэширование

Этот последний шаг также необязателен, но может помочь в обеспечении устойчивости платформы и времени отклика. Короче говоря, мы будем использовать какую-либо форму сверхбыстрой кэширующей базы данных, например Redis, для хранения данных запроса после первого запроса & с тех пор после обслуживания кэшированных данных вместо необходимости делать несколько запросов.

Для своего сайта я в итоге использовал Upstash, который предлагает одну единственную базу данных Redis бесплатно. Я рекомендую следовать их руководству, чтобы начать настройку вашей первой базы данных Redis.

После настройки базы данных у вас должен быть REDIS_URL для добавления вашего файла .env. Он должен выглядеть примерно так rediss://USERNAME:PASSWORD@HOST:PORT. Не волнуйтесь, если имя пользователя отсутствует.

GITHUB_PAT=abc123
REDIS_URL=rediss://USERNAME:PASSWORD@HOST:PORT
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь, когда у нас есть база данных, нам нужен клиент для доступа к ней. Существует несколько клиентов Redis, включая собственный клиент Upstash, но лично я предпочитаю ioredis, поэтому именно его я буду использовать в этом примере, но любой другой будет работать нормально. Установите его в качестве зависимости для разработки.

npm install --save-dev ioredis
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Или используя Yarn

yarn add -D ioredis
Войдите в полноэкранный режим Выйти из полноэкранного режима

Установив его, мы можем создать новый экземпляр клиента для подключения к нашей базе данных.

// portfolio.jsx
export async function getServerSideProps () {
    const redis = new Redis(process.env.REDIS_URL);
    // ...
};
Вход в полноэкранный режим Выход из полноэкранного режима

Наконец, мы можем добавить логику для проверки того, есть ли в кэше данные, которые нам нужны & если нет, мы можем получить свежие данные из GitHub & хранить их в кэше, прежде чем мы вернем их обратно.

// portfolio.jsx
export async function getServerSideProps () {
    // ...
    const cache = await redis.get('repositories');
    if (cache !== null)
        return {
            props: {
                repositories: cache,
            }
        }
    const repositories = ...
    redis.set('repositories', JSON.stringify(repositories));
    return {
        props: {
            repositories,
        }
    }
};
Вход в полноэкранный режим Выход из полноэкранного режима

✅ Заключение

Вот и все, у нас есть очень простая, но надежная система, которая позволяет нам использовать GitHub для управления содержимым, отображаемым на нашем сайте, но при этом не полностью зависит от GitHub, поэтому если по какой-то причине произойдет простой на GitHub, у нас все равно будут кэшированные данные.

// portfolio.jsx
export async function getServerSideProps () {
    const redis = new Redis(process.env.REDIS_URL);
    const cache = await redis.get('repositories');
    if (cache !== null)
        return {
            props: {
                repositories: cache,
            }
        }
    const response = await fetch('https://api.github.com/users/GITHUB_USERNAME/repos', {
        headers: {
            authorization: `token ${process.env.GITHUB_PAT}`,
        },
    });
    const json = await response.json();
    const repositories = json.map((repo) => {
        if (!repo.topics.includes('portfolio')) return null;
        if (repo.archived) return null;
        return repo;
    })
    .filter((project) => project !== null);
    redis.set('repositories', JSON.stringify(repositories));
    return {
        props: {
            repositories,
        }
    }
};
export default function Page({ repositories }) {
    return (
        <pre>
            {JSON.stringify(repositories, null, 4)}
        </pre>
    );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *