Легкий скорочувач посилань на JavaScript, Cloudflare Workers і з Telegram-ботом

Habr Технічна тема Мова оригіналу: Російською

Покрокове створення особистого скорочувача посилань на власному піддомені за допомогою Cloudflare Workers і KV. Нові скорочення додаються через Telegram-бота, а стаття наводить код і тогочасні обмеження сервісу.

Translated by ChatGPT GPT-6.1 Sol

Відомості про першоджерело

Легкий сокращатель ссылок на JavaScript, Cloudflare Workers и c Telegram Bot'ом

Архівна публікація. Посади, показники й технічні інструкції наведено станом на дату першої публікації.

Що робити, якщо потрібно швидко створити коротке посилання? Звісно — використати скорочувач посилань. А що, як при цьому зробити це посилання читабельним? Ще й використати свій особистий домен? А краще було б робити це без додаткових серверів. Схоже, відповідь є.


Передісторія

Ідея «легкого скорочувача посилань» прийшла до мене в момент пошуку варіанта переадресації з використанням домену для однієї з кімнат у новомодній соцмережі Clubhouse. Суть ідеї переадресації для кімнати полягала в тому, щоб перезапускати кімнату з однаковою назвою, але постійною присутністю онлайн. Потрібно було розв’язати проблему постійної зміни адреси кімнати, припаркувавши таке посилання до піддомену.

Рішення напрошувалося саме собою, оскільки сайт заздалегідь було підключено до Cloudflare. Спочатку використовувалася функція «Page Rules», яка дає змогу задати, зокрема, правила переадресації, але невдовзі прийшла ідея зробити цю переадресацію гнучкішою й змінюваною без потреби заходити в налаштування сервісу. Звісно ж, таким рішенням став Telegram Bot.

Постановка завдання

Щоб виконати задумане, потрібно розв’язати кілька проблем:

  • Як переадресовувати з певного піддомену?

  • Куди зберігати посилання за ключем (скорочення) — значенням (адреса переадресації)?

  • За допомогою чого створювати такі скорочення?

Як ви здогадалися, відповіді на ці питання є в самому заголовку статті. Тому пропоную перейти до практичної частини.

Передумови

Для докладнішого опису зазначу основні умови, необхідні для реалізації нашого проєкту:

  • Домен, підключений до Cloudflare;

  • Загальні знання JavaScript;

  • Створений Telegram-бот;

  • Документація щодо Cloudflare Workers і Telegram Bot API.

Як виконати необхідні передумови, у цій статті не розглядається. Розв’язання цих завдань залишається за читачем.

Підготовка

Здавалося б, усі передумови дотримано — «Яка ще підготовка?». Пропоную відзначити кілька кроків підготовки до реалізації:

1. Створення сховища — нам допоможеCloudflare KV.

Cloudflare KV — це база даних для Workers за принципом «ключ — значення». Як ви зрозуміли, друга проблема розв’язалася силами самого Cloudflare.

Послідовність проста: на сторінці наших Workers переходимо у вкладку KV, вводимо бажане ім’я для сховища, натискаємо додати.

Сторінка Cloudflare KV

Сторінка Cloudflare KV

У результаті можемо навіть подивитися, що знаходиться всередині нашого сховища. Не дивно, що нічого, але ми можемо завантажити сюди наші бажані скорочення безпосередньо. Це може знадобитися для початку роботи з ними, наприклад, щоб спочатку протестувати переадресацію.

Усередині нашого сховища

Усередині нашого сховища

2. Створюємо свій Worker і налаштовуємо його.

Для цього скористаємося кнопкою «Create worker», у редакторі одразу збережемо й розгорнемо новий Worker («Save and Deploy») і повернемося назад у меню.

Такий вигляд має сторінка нового Worker

Такий вигляд має сторінка нового Worker

Одразу задаємо зрозуміле ім’я й, перейшовши в «Settings», запишемо токен нашого Telegram-бота, а також прив’яжемо сховище.

Налаштування Worker

Конфіденційне значення приховано на цій архівній ілюстрації.

Налаштування Worker

3. Прив’яжемо піддомен до скрипту

Щоб звернення за бажаною адресою, у моєму випадку url.mydomain.com, спрямовувало користувача в наш майбутній «сервіс-скорочувач», налаштуємо прив’язку до піддомену.

Сторінка Workers для нашого домену

Сторінка Workers для нашого домену

А саме, нам треба на сторінці «Workers» для нашого домену додати свій «Route» на майбутній сервіс-скорочувач.

Додавання нового перенаправлення

Додавання нового перенаправлення

Зауважте, що зірочка в кінці посилання означає, що будь-яке значення після нашого домену (path — шлях) спрямовуватиметься в скорочувач.

Це важливий аспект, щоб далі все спрацювало.

Відповідно, створюємо також запис у DNS, щоб усі запити проходили DNS-перевірку.

Новий запис у DNS, за яким знаходитиметься скорочувач посилань

Новий запис у DNS, за яким знаходитиметься скорочувач посилань

Готово! Можемо переходити до написання коду.

Реалізація

Перейдімо до безпосередньої реалізації. Подальші дії відбуватимуться в редакторі коду, який надає нам Cloudflare. Ми його вже бачили перед тим, як ініціалізувати новий Worker. Повернімося туди, скориставшись кнопкою «Quick edit» на сторінці нашого проєкту.

Редактор коду

Редактор коду

У нас сервіс складатиметься з двох частин:

  • Переадресація

  • Запис нового скорочення

Для реалізації переадресації напишемо функцію, яка братиме значення з нашої бази даних і в разі знаходження введеного нами шляху (URL path) створюватиме редирект. В іншому випадку видаватимемо помилку 404.

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

/**
 * Respond to the request
 * @param {Request} request
 */
async function handleRequest(request) {
  const requestUrl = new URL(request.url);
  const path = requestUrl.pathname.substring(1); // Удалим символ "/"
  return await redirect(path)
}

/**
 * Make redirect
 * @param {string} shortName
 */
async function redirect(shortName) {
  // Получим значение адреса который запросили по короткой ссылке
  const url = await db.get(shortName);
  if (url) {
    // Переадресовываем по ссылке
    return Response.redirect(url)
  }
  // Не нашли такого сокращения
  return new Response(null, {status: 404})
}

Тут же, у правій половині редактора, яка дає змогу налагоджувати ще не розгорнутий код, перевіряємо роботу переадресації:

Є контакт!

Є контакт!

Тепер перейдемо до реалізації другої частини. Тут завдання буде об’ємнішим. Для початку ми визначатимемо, що це до нас постукав Telegram через заданий нами URL. Далі перевіримо, що це боту написали ми: щоб ніхто інший не мав доступу до бота, пропишемо наш Telegram User ID у константу. Наступним кроком дістанемо з надісланого повідомлення короткий шлях і посилання, куди переадресовувати, та запишемо посилання в базу даних. Наприкінці підключимо нашого бота через вебхуки.

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

const ADMIN = 11111111; // Типо наш Telegram User ID

/**
 * Respond to the request
 * @param {Request} request
 */
async function handleRequest(request) {
  const requestUrl = new URL(request.url);
  const path = requestUrl.pathname.substring(1);
  // Добавили проверку пути на соответствие токену нашего бота
  if (path == BOT_TOKEN) {
    return await bot(await request.json())
  }

  return await redirect(path)
}

/**
 * Make redirect
 * @param {string} shortName
 */
async function redirect(shortName) {
  const url = await db.get(shortName);
  if (url) {
    return Response.redirect(url)
  }
  return new Response(null, {status: 404})
}

/**
 * Create new shorten URL
 * @param {Object} update
 */
async function bot(update) {
  // Пропускаем сообщения от НЕ админов
  if (update.message.from.id != ADMIN) {
    return new Response("OK", {status: 200})
  }
  // Разбиваем сообщения типа "shortname url"
  const [shortName, url] = update.message.text.split(" ");
  // Запоминаем наше новое сокращение
  await db.put(shortName, url);
  const response = {
    // Что мы хотим сделать в ответ на новое сообщение
    "method": "sendMessage",
    // Чем будем отвечать на новую пару сокращение - адрес переадресации
    "text": `Теперь ${url} доступно по короткой ссылке url.mydomain.com/${shortName}`,
    // Кому будем отвечать, тут может быть просто ADMIN (идентично нынешней реализации), 
    // либо update.message.chat.id чтобы у администратора была возможность 
    // использовать бота в групповых чатах
    "chat_id": update.message.from.id
  }

  return new Response(
    JSON.stringify(response), 
    {
      status: 200,
      headers: new Headers({"Content-Type": "application/json"})
    }
  )
}

Тут же, у засобах налагодження, перевіряємо роботу нашого коду:

Виглядає працездатним

Виглядає працездатним

Загляньмо в нашу базу даних, щоб переконатися, що все записалося (тут же можемо почистити сховище від наших тестових значень):

Працює

Працює

Залишилася дрібниця — повісити на нашу сторінку Telegram Bot Webhook. У нас для цього все готово, тому скористаємося посиланням формату:

https://api.telegram.org/bot[BOT_TOKEN]/setWebhook?url=url.domain.com/[BOT_TOKEN]

Відповідь Telegram API має бути:

{"ok":true,"result":true,"description":"Webhook was set"}

Перевіряємо результат роботи бота. Надсилаємо йому коротке ім’я та посилання, як задали в коді, і пробуємо перейти за посиланням для перевірки працездатності.

«Він живий!»

Висновок

У підсумку в нас вийшов короткий і легкий у реалізації «скорочувач посилань», який ми можемо модифікувати на власний розсуд.

Варто зазначити, що такий підхід має деякі обмеження, з якими можна ознайомитися на сторінці Cloudflare Workers. Якщо коротко, то:

  • записувати в БД ми можемо до 1000 значень на день (максимально можлива кількість створених скорочень);

  • зчитувати з БД до 100 000 разів на день (максимальна кількість відвідувань);

  • сам скрипт може бути запущений до 100 000 разів на день (кількість повідомлень боту й відвідувань скорочених посилань);

  • скрипт має запускатися не більше 1000 разів на хвилину.

Цих обмежень має вистачити для особистого використання, поділіться вашою думкою з цього приводу в коментарях.