Делаем простую статистику и лайки за 5 минут, используя воркеры Cloudflare с применеием Key Value хранилища от них же.

Задача:

После перезда с medium.com на свой собственный блог (в качестве движка которого я использую Ghos 3й версии, так как он мне очень нравится) я не смог найти чего-то удобного для статистики и лайков. Все же хорошей мотивацией является то, что мои статьи кому-то приносят пользу и интересны, так что если не сложно - ставьте лайк :)

И так, готового плагина не нашел, но и писать свой бэкенд как-то не очень-то и хотелось. И тут я подумал, а почему бы не заюзать воркеры для этой задачи + KV от моего любимого Cloudflare?

Первое что нам нужно - завести два неймпсейса - по сути две таблицы:

Тут вроде бы все просто. Далее создаем два воркера. Один воркер считает статистику просмотров, второй воркер обрабатывает "хлопки" (в терминах medium.com :))

На вкладке воркеров создаем два воркера. Код воркеров показан ниже.

Код воркера для обработки лайков. Заходим в редактор:

и пишем код:


addEventListener('fetch',e=>{e.respondWith(handleRequest(e.request))});

const res = json =>
    new Response(
        JSON.stringify(json),
        {
            status: 200,
            headers: [
                ['Content-Type', 'application/json'],
                [
                   'Access-Control-Allow-Origin',
                   'https://tech.geekjob.ru'
                ]
            ]
        }
    )
;


async function handleRequest(req) {
    let url = new URL(req.url);
    let key = url.searchParams.get('k');

    if (!key)
        return res({ error: true, message: 'Bad key' });

    let likes = parseInt(await GJ_BLOG_LIKES.get(key)) || 0;
    await GJ_BLOG_LIKES.put(key, ++likes);

    return res({ likes })
}

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

Код простой - обращаемся к нашему хранилищу, читаем данные, приводим к числу, увеличиваем счетчик и возвращаем ответ.

KV поддерживает всего 3 типа:

  • string
  • ReadableStream
  • ArrayBuffer

При этом при чтении вы можете указать какой тип возвращается и там есть возможность сразу преобразовать JSON строку в объект:

NAMESPACE.get(key, type)

The type parameter can be any of:

  • "text": (default) a string
  • "json": an object decoded from a JSON string
  • "arrayBuffer": An ArrayBuffer instance.
  • "stream": A ReadableStream.

Собственно поэтому мы используем parseInt() для преобразования числа в Number.

Если вы попытаетесь запустить ваш воркер - он выдаст ошибку, так как не знает ничего про ваше хранилище еще.

Подключаем KV хранилище

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

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

Вот теперь ваш воркер будет работать.

Пишем код воркера для подсчета просмотров и возврата статистики

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


addEventListener('fetch',e=>{e.respondWith(handleRequest(e.request))});

const res = json =>
    new Response(
        JSON.stringify(json),
        {
            status: 200,
            headers: [
                ['Content-Type', 'application/json'],
                [
                    'Access-Control-Allow-Origin',
                    'https://tech.geekjob.ru'
                ]
            ]
        }
    )
;


async function handleRequest(req) {
    let url = new URL(req.url);
    let key = url.searchParams.get('k');

    if (!key)
        return res({ error: true, message: 'Bad key' });

    let views = parseInt(await GJ_BLOG_STAT.get(key)) || 0;
    await GJ_BLOG_STAT.put(key, ++views);
    let likes = parseInt(await GJ_BLOG_LIKES.get(key)) || 0;

    return res({
        views,
        likes
    })
}

Так же не забываем пробросить KV внутрь воркера через настройки.

Теперь в админке Ghost, через блок Code Injections я добавил стили и небольшой JS код.

Site Header. Code here will be injected into the {{ghost_head}} tag on every page of the site
<style>
#statinfo {
    cursor: pointer;
    z-index: 99999;
    background: #fff;
    position: absolute;
    width: 70px;
    top: 69px;
    right: 2%;
    color: #000;
    font-size: 12px;
    text-align:center;
    padding: 4px;
    border-radius: 2px;
    opacity: .7;
    font-weight: bold;
}
#statinfo hr {
    height: 1px;
    line-height: 1px;
    font-size: 1px;
    padding: 0;
    margin: 2px 0;
    border-top: 1px solid #000;
}
</style>
Site Footer. Code here will be injected into the {{ghost_foot}} tag on every page of the site
<!-- Код визуального блока кнопки Like со статистикуой -->
<div id="statinfo" onclick="likePost()">
    👍 <span class="silikes">?</span>
    <hr>
    👓 <span class="siviews">?</span>
</div>
<script>
// Создаем имя страницы - по сути только путь без крайних слешей
var pageid = location.pathname.replace('/','').replace(/\/$/,'') || 'main';

document.addEventListener('DOMContentLoaded', function(){
    var $statinfo = document.querySelector('#statinfo'),
        $views = $statinfo.querySelector('.siviews'),
        $likes = $statinfo.querySelector('.silikes')
    ;

    // Обрабатываем Like
    window.likePost = function() {
        fetch('https://like-blog.geekjob.workers.dev/?k='+pageid)
            .then(d=>d.json())
            .then(data => {
                if (data.error) return;
                $likes.innerText = data.likes || 0;
            })
            .catch(console.error);        
    };

    // Засчитываем просмотр страницы и загружаем статистику
    fetch('https://stat-blog.geekjob.workers.dev/?k='+pageid)
        .then(d=>d.json())
        .then(data => {
            if (data.error) return;
            $views.innerText = data.views || 0;
            $likes.innerText = data.likes || 0;
	    })
        .catch(console.error);
})
</script>

Собственно что получилось:

Результат вы можете видеть на этой странице если все отработало штатно, без ошибок.

Поставьте лайк, если не сложно :)

Спасибо!

Больше информации в документации:

KV
Use Cloudflare’s APIs and edge network to build secure, ultra-fast applications.
Quick Start
Use Cloudflare’s APIs and edge network to build secure, ultra-fast applications.