Персонализированные короткие URL выглядят так здорово. Конечно, я мог бы воспользоваться такой услугой, как Bitly. Они даже предоставляют возможность использовать ваш домен. Но я люблю создавать вещи, даже когда мне это не нужно. Это позволяет мне исследовать и узнавать новое.
Я понял, что мне нужен короткий URL, когда начал делиться постами со своего сайта baldbeardedbuilder.com. Он длинный, и это без учета слизней. Поэтому я отправился на поиски чего-то суперкороткого и нашел bbb.dev. Это идеально! Я быстро купил его и приступил к работе.
Хранение URL-адресов
Первым делом нужно было сохранить пары коротких и длинных URL. Я знал, что мне понадобится база данных, и, использовав в прошлом Fauna, решил, что она хорошо подойдет для этого приложения. На моем сайте не так много трафика, поэтому я рассчитывал, что их бесплатный уровень меня устроит.
Обновление: эта функция работает уже более года, а я все еще не полностью использую бесплатный тариф Fauna.
Структура данных
В своем аккаунте Fauna я создал новую базу данных под названием smolify
с коллекцией под названием ShortyMcShortLink
. Почему такие имена? Потому что мне нравится думать, что мой мозг, как в шутке про отца, умный. 😁
Затем я добавил начальную запись. Я знал, что ей нужен короткий код и полный URL, но я также подумал, что было бы здорово отслеживать, сколько посещений было использовано по короткому URL. Поэтому я добавил свойство visits
, чтобы отслеживать это. Мой первый короткий URL использовался для кодекса поведения сообщества BBB и выглядел следующим образом
следующим образом:
{
"source": "coc",
"target": "https://baldbeardedbuilder.com/code-of-conduct/",
"visits": 0
}
Всякий раз, когда мне нужен новый короткий URL, я вхожу в Fauna и создаю новый документ в этом формате с соответствующими source
и target
.
Извлечение данных
Имея данные в моей коллекции, мне нужен был способ захвата определенных записей на основе свойства source
. Для этого я создал индекс с именем shortyMcShortLinkBySource
. Еще раз прошу прощения за мои навеянные отцовской шуткой условности именования. 🙄
У этого индекса есть один «термин», за которым он следит; свойство source
.
Создание функции Netlify
Когда база данных была готова, я перешел к созданию бессерверной функции для получения и обновления данных. Мой сайт в настоящее время размещен на Netlify, поэтому их функции показались мне отличным местом для начала.
Я создал новый файл JavaScript с именем smolify.js
и добавил свои зависимости.
import { Client, query } from 'faunadb';
require('dotenv').config()
exports.handler = async (event) => {
}
Поиск короткого кода
Сначала мне нужно было найти короткий код и получить длинный URL. Для этого я создал функцию getLongUrl
. Она принимает путь (в данном случае короткий код) и находит его в индексе, который я ранее установил.
Функция mapResponse
выполняет небольшое отображение, чтобы легче было определить id
, присвоенный Fauna для последующего использования, а затем возвращает запись, которую нашла Fauna. Если что-то пошло не так, возвращается значение undefined.
const getLongUrl = async (path) => {
try {
const response = await client.query(
query.Map(
query.Paginate(
query.Match(
query.Index("shortyMcShortLinkBySource"), path
)
),
query.Lambda("ShortyMcShortLink", query.Get((query.Var("ShortyMcShortLink"))))
)
)
if (response.data && response.data.length > 0) {
const shortUrl = mapResponse(response.data[0])
return shortUrl
}
} catch (err) {
console.log(err)
}
return undefined
}
function mapResponse(payload) {
return {
...payload.data,
_id: payload.ref.value.id
}
}
Подсчет посещений
Прежде чем отправить результат обратно в браузер, я хочу записать посещение Fauna. Для этого я создал приведенную ниже функцию recordVisit
. Она увеличивает счетчик посещений, который предоставила Fauna, а затем заменяет этот объект на основе Fauna id
, который мы сопоставили.
const recordVisit = async (shortUrl) => {
try {
shortUrl.visits++
await client.query(
query.Replace(query.Ref(query.Collection("ShortyMcShortLink"), shortUrl._id), {
data: shortUrl
})
)
} catch (err) {
console.log(err)
}
}
Затем я обновил функцию getLongUrl
, чтобы добавить вызов recordVisit
между отображением и возвратом.
const shortUrl = mapResponse(response.data[0])
await recordVisit(shortUrl)
return shortUrl
Завершение работы нашей функции
Когда JavaScript готов, я добавил его в функцию-обработчик. Сначала он вызывает функцию getLongUrl
с event.queryStringParameters.path
. Я расскажу об изменении /coc
на /?path=coc
позже.
Если функция может найти короткий URL, переменная redirectUrl
устанавливается в свойство target
. Если она возвращает неопределенное значение, функция перенаправляет человека на домашнюю страницу сайта baldbeardedbuilder.com.
exports.handler = async (event, context) => {
const shortUrl = await getLongUrl(event.queryStringParameters.path)
const redirectUrl = shortUrl ? shortUrl.target : 'https://baldbeardedbuilder.com/'
return {
statusCode: 302,
headers: {
location: redirectUrl,
'Cache-Control': 'no-cache',
},
body: JSON.stringify({}),
}
}
Настройка Netlify
Осталось только перенаправить трафик с помощью короткого URL на функцию. К счастью, Netlify делает это довольно быстрым процессом. Сначала я добавил домен в свое приложение Netlify. После того как он был настроен и отправлял трафик на сайт, я настроил перенаправление в Netlify.
Перенаправление короткого URL
В корне сайта я добавил файл netlify.toml
со следующим фрагментом:
[[redirects]]
from = "https://bbb.dev/*"
to = "/.netlify/functions/smolify?path=:splat"
status = 301
force = true
Это принимает весь трафик на https://bbb.dev
и перенаправляет его на функцию smolify
. При перенаправлении он добавляет splat из фильтра from
в качестве параметра querystring с именем path
.
Помните параметр path
, который я захватывал в функции? Вот откуда он берется.
Завершение работы
Затем я развернул все на сайте. Весь трафик на короткий URL перенаправляется на функцию Netlify, и я могу добавить столько коротких кодов, сколько мне нужно, добавив запись в базу данных Fauna.