Что делать если нужно быстро создать короткую ссылку? Конечно – использовать сокращатель ссылок. А что если при этом сделать эту ссылку читаемой? Еще и при этом использовать свой личный домен? А лучше было бы делать это без дополнительных серверов. Похоже что ответ есть.
Предыстория
Идея «легкого сокращателя ссылок» пришла ко мне в момент поиска варианта переадресации с использованием домена для одной из комнат в новомодной соцсети Clubhouse. Суть идеи переадресации для комнаты состоял в том, чтобы перезапускать комнату с одинаковым названием, но постоянным онлайном. Решить нужно было проблему постоянной смены адреса комнаты припарковав такую ссылку к под-домену.
Решение напросилось само собой, так как сайт был заранее посажен на Cloudflare. Изначально использовала функция «Page Rules», которая позволяет задать, в том числе, правила переадресации, но вскоре пришла идея сделать эту переадресацию более гибкой и изменяемой без нужды заходить в настройки сервиса. Конечно же, таким решением стал Telegram Bot.
Постановка задачи
Для того чтобы выполнить задуманное нужно решить несколько проблем:
Как переадресовывать с определенного под-домена?
Куда сохранять ссылки по ключу (сокращение) – значению (адрес переадресации)?
С помощью чего создавать такие сокращения?
Как вы догадались, ответы на эти вопросы есть в самом заголовке статьи. Поэтому предлагаю приступить к практической части.
Предусловия
Для более детального описания отмечу основные условия необходимые для реализации нашего проекта:
Домен подключенный к Cloudflare;
Общие знания JavaScript;
Созданный Telegram бот;
Документация по Cloudflare Worker'ам и Telegram Bot API.
Как выполнить необходимые предусловия в этой статье не рассматривается. Решение этих задач остается на читателе.
Подготовка
Казалось бы, все предусловия соблюдены, — «Какая еще подготовка?». Предлагаю отметить несколько шагов подготовки к реализации:
1. Создание хранилища – нам поможет Cloudflare KV.
Cloudflare KV — это база данных для Worker'ов по принципу «ключ - значение». Как вы поняли, вторая проблема решилась силами самого Cloudflare.
Последовательность простая: на странице наших Workers переходим во вкладку KV, вводим желаемое имя для хранилища, нажимаем добавить.
![Страница Cloudflare KV Страница Cloudflare KV](https://habrastorage.org/getpro/habr/upload_files/814/04e/fb7/81404efb74bb88fca0f0d788320eec1a.png)
По результату можем даже посмотреть что находится внутри нашего хранилища. Не удивительно что ничего, но мы можем сюда загрузить наши желаемые сокращения напрямую. Это может понадобиться для старта работы с ними, например, протестировать сначала переадресацию.
![Внутри нашего хранилища Внутри нашего хранилища](https://habrastorage.org/getpro/habr/upload_files/fb2/525/2c9/fb25252c9229be330d3dadd1cad84d5b.png)
2. Создаем свой Worker и настраиваем его.
Для этого воспользуемся кнопкой «Create worker», в редакторе сразу сохраним и задеплоим новый Worker («Save and Deploy») и вернемся обратно в меню.
![Так выглядит страница нового Worker'а Так выглядит страница нового Worker'а](https://habrastorage.org/getpro/habr/upload_files/00a/7bc/a71/00a7bca718afa68f2b428c2e5096237b.png)
Сразу задаем вменяемое имя и перейдя в «Settings» запишем токен нашего Telegram бота, а также привяжем хранилище.
![Настройки Worker'а Настройки Worker'а](https://habrastorage.org/getpro/habr/upload_files/630/be5/5ef/630be55ef7f8c3c7485dc7150c14ce25.png)
3. Привяжем под-домен к скрипту
Для того чтобы обращение по желаемому адресу, в моем случае url.mydomain.com
, направляло пользователя в наш будущий «сервис-сокращатель» настроим привязку к под-домену.
![Страница Workers для нашего домена Страница Workers для нашего домена](https://habrastorage.org/getpro/habr/upload_files/620/4c4/4fa/6204c44fa8ab96bda24ac4ef1c83d61e.png)
А именно, нам надо на странице «Workers» для нашего домена добавить свой «Route» на будущий сервис-сокращатель.
![Добавление нового перенаправления Добавление нового перенаправления](https://habrastorage.org/getpro/habr/upload_files/225/f3d/bb8/225f3dbb8962772129c709e6926e7155.png)
Заметьте, что звездочка вконце ссылки обозначает то, что любое значения после нашего домена (path - путь) будет направляться в сокращатель.
Это важный аспект для того чтобы дальше все сработало.
Соответственно, создаем также запись в DNS чтобы все запросы проходили DNS проверку.
![Новая запись в DNS по которой будет находиться сокращатель ссылок Новая запись в DNS по которой будет находиться сокращатель ссылок](https://habrastorage.org/getpro/habr/upload_files/359/b6b/406/359b6b4060c24c3d0cd81943dc1d9d1c.png)
Готово! Можем приступать к кодированию.
Реализация
Приступим к непосредственной реализации. Дальнейшие действия будут происходить в редакторе кода, который предоставляет нам Cloudlfare. Мы его уже видели перед тем как инициализировать новый Worker. Вернемся туда воспользовавшись кнопкой «Quick edit» на странице нашего проекта.
![Редактор кода Редактор кода](https://habrastorage.org/getpro/habr/upload_files/cb9/01e/b1b/cb901eb1b778d922ffd4404b4efbc6f2.png)
У нас сервис будет состоять из двух частей:
Переадресация
Запись нового сокращения
Для реализации переадресации напишем функцию которая будет брать значение из нашей базы данных и в случае нахождения введенного нами пути (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})
}
Тут же, в правой половине редактора который позволяет сделать дебаг еще не задеплоенного код, проверяем работу переадресации:
![Есть контакт! Есть контакт!](https://habrastorage.org/getpro/habr/upload_files/dae/cb5/4be/daecb54be490525e6a9ed17e1501f0fa.png)
Теперь приступим к реализации второй части. Тут задача будет более объемной. Для начала мы будем определять что это к нам постучался Telegram через заданный нами URL. Дальше проверим что это боту написали мы, чтобы никто другой не имел доступа к боту пропишем наш Telegram User ID в константу. Следующим шагом достанем из присланного сообщения короткий путь и ссылку куда переадресовывать и запишем ссылку в базу данных. В конце подключим нашего бота через webhook'и.
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"})
}
)
}
Тут же, в дебаге, проверяем работу нашего кода:
![Выглядит работоспособно Выглядит работоспособно](https://habrastorage.org/getpro/habr/upload_files/0f4/8ad/312/0f48ad3127d908f7bda559b66215cb24.png)
Заглянем в нашу базу данных чтобы убедиться что все записалось (тут же можем почистить хранилище от наших тестовых значений):
![Работает Работает](https://habrastorage.org/getpro/habr/upload_files/9f3/5a6/5a7/9f35a65a784d845b775cc8f3b3ceff19.png)
Осталось дело за малым – повесить на нашу страницу 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"}
![](https://habrastorage.org/getpro/habr/upload_files/883/d5b/bde/883d5bbdef774e3b490d4ed220fff1cb.png)
Проверяем результат работы бота. Отправляем ему короткое имя и ссылку, так как задали в коде, и пробуем пройти по ссылке для проверки работоспособности.
«Он жив!»
Заключение
В итоге у нас получился короткий и легкий в реализации «сокращатель ссылок» который мы можем модифицировать по нашему усмотрению.
Стоит отметить, что такой подход имеет некоторые ограничения с которыми можно ознакомится на странице Cloudflare Worker'ов. Если коротко, то:
записывать в БД мы можем до 1000 значений в день (максимально возможное количество созданных сокращений);
считывать из БД до 100 000 раз в день (максимальное количество посещений);
сам скрип может быть запущен до 100 000 раз в день (количество сообщений боту и посещений сокращенных ссылок);
скрипт должен запускаться не больше 1000 раз в минуту.
Этих ограничений должно хватить для личного использования, поделитесь вашим мнением на этот счет в комментариях.