Сокращатель ссылок с помощью консоли управления
Чтобы создать сокращатель ссылок с помощью консоли управления Yandex Cloud:
- Подготовьте облако к работе.
- Настройте хостинг страницы сокращателя.
- Создайте сервисный аккаунт.
- Создайте БД в Yandex Managed Service for YDB.
- Настройте функцию в Yandex Cloud Functions.
- Опубликуйте сервис через Yandex API Gateway.
- Проверьте работу сокращателя.
Если созданные ресурсы вам больше не нужны, удалите их.
Подготовьте облако к работе
Зарегистрируйтесь в Yandex Cloud и создайте платежный аккаунт:
- Перейдите в консоль управления
, затем войдите в Yandex Cloud или зарегистрируйтесь. - На странице Yandex Cloud Billing
убедитесь, что у вас подключен платежный аккаунт, и он находится в статусеACTIVEилиTRIAL_ACTIVE. Если платежного аккаунта нет, создайте его и привяжите к нему облако.
Если у вас есть активный платежный аккаунт, вы можете создать или выбрать каталог, в котором будет работать ваша инфраструктура, на странице облака
Подробнее об облаках и каталогах.
Необходимые платные ресурсы
В стоимость поддержки инфраструктуры для сокращателя ссылок входят:
- плата за хранение данных (см. тарифы Yandex Object Storage);
- плата за операции с базой данных YDB и хранение данных (см. тарифы Managed Service for YDB);
- плата за количество вызовов функции, вычислительные ресурсы, выделенные для выполнения функции, и исходящий трафик (см. тарифы Cloud Functions);
- плата за количество запросов к API-шлюзу и исходящий трафик (см. тарифы API Gateway).
Настройте хостинг страницы сокращателя
Чтобы создать бакет, разместить в нем HTML-страницу вашего сервиса и настроить бакет для хостинга статических сайтов:
-
В консоли управления
выберите свой рабочий каталог. -
Выберите сервис Object Storage.
-
Нажмите кнопку Создать бакет.
-
На странице создания бакета:
-
Введите имя бакета.
Важно
Имена бакетов уникальны для всего Object Storage, т. е. нельзя создать два бакета с одинаковыми именами даже в разных каталогах разных облаков.
-
Задайте максимальный размер
1 ГБ. -
Выберите доступ на чтение объектов
Для всех. -
Нажмите кнопку Создать бакет для завершения операции.
-
-
Скопируйте HTML-код и вставьте его в файл
index.html:HTML-код
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Сокращатель URL</title> <!-- предостережет от лишнего GET запроса на адрес /favicon.ico --> <link rel="icon" href="data:;base64,iVBORw0KGgo="> </head> <body> <h1>Добро пожаловать</h1> <form action="javascript:shorten()"> <label for="url">Введите ссылку:</label><br> <input id="url" name="url" type="text"><br> <input type="submit" value="Сократить"> </form> <p id="shortened"></p> </body> <script> function shorten() { const link = document.getElementById("url").value fetch("/shorten", { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: link }) .then(response => response.json()) .then(data => { const url = data.url document.getElementById("shortened").innerHTML = `<a href=${url}>${url}</a>` }) .catch(error => { document.getElementById("shortened").innerHTML = `<p>Произошла ошибка ${error}, попробуйте еще раз</p>` }) } </script> </html> -
Нажмите на имя созданного бакета.
-
Нажмите кнопку Загрузить объекты.
-
Укажите подготовленный ранее файл
index.html. -
Нажмите кнопку Загрузить.
-
На панели слева выберите вкладку Веб-сайт.
-
Выберите опцию Хостинг.
-
Укажите главную страницу сайта —
index.html. -
Нажмите кнопку Сохранить.
Создайте сервисный аккаунт
Чтобы создать сервисный аккаунт для взаимодействия компонентов сервиса:
-
Перейдите в свой рабочий каталог.
-
В верхней части экрана перейдите на вкладку Сервисные аккаунты.
-
Нажмите кнопку Создать сервисный аккаунт.
-
Введите имя сервисного аккаунта
serverless-shortener. -
Нажмите Добавить роль и выберите роль
editor. -
Нажмите кнопку Создать.
-
Нажмите на имя созданного сервисного аккаунта.
Сохраните идентификатор созданного сервисного аккаунта, он понадобится на следующих шагах.
Создайте БД в Managed Service for YDB
Чтобы создать базу данных Managed Service for YDB и настроить ее для хранения ссылок:
-
Перейдите в свой рабочий каталог.
-
В списке сервисов выберите Managed Service for YDB.
-
Нажмите кнопку Создать базу данных.
-
Введите имя базы
for-serverless-shortener. -
Выберите тип базы данных Serverless.
-
Нажмите кнопку Создать базу данных.
-
Дождитесь запуска базы данных.
В процессе создания база будет иметь статус
Provisioning, а когда станет готова к использованию, статус изменится наRunning. -
Нажмите на имя созданной БД.
Сохраните значение поля Эндпоинт, оно понадобится на следующих шагах.
-
На панели слева выберите вкладку Навигация.
-
Выберите Создать → Таблицу в правой части страницы.
-
Настройте параметры таблицы:
- Имя таблицы —
links. - Тип таблицы —
Строковая таблица.
- Имя таблицы —
-
Добавьте колонки:
- имя колонки —
id, тип данных —Utf8и установите Первичный ключ. - имя колонки —
link, тип данных —Utf8.
- имя колонки —
-
Нажмите кнопку Создать таблицу.
Настройте функцию в Cloud Functions
Чтобы создать и настроить функцию сокращения URL:
-
Перейдите в свой рабочий каталог.
-
В списке сервисов выберите Cloud Functions.
-
Нажмите кнопку Создать функцию.
-
Введите имя
for-serverless-shortener. -
Нажмите кнопку Создать.
-
В выпадающем списке Python выберите среду выполнения
python312. -
Нажмите кнопку Продолжить.
-
Скопируйте код функции и вставьте его в файл
index.pyв блоке Код функции.Код функции
import ydb import urllib.parse import hashlib import base64 import json import os import traceback def decode(event, body): is_base64_encoded = event.get('isBase64Encoded') if is_base64_encoded: body = str(base64.b64decode(body), 'utf-8') return body def response(statusCode, headers, isBase64Encoded, body): # Всегда отдаём строку в body if not isinstance(body, str): body = json.dumps(body, ensure_ascii=False) return { 'statusCode': statusCode, 'headers': headers, 'isBase64Encoded': isBase64Encoded, 'body': body, } def get_config(): endpoint = os.getenv("endpoint") database = os.getenv("database") if endpoint is None or database is None: raise AssertionError("Нужно указать обе переменные окружения: endpoint и database") credentials = ydb.iam.MetadataUrlCredentials() return ydb.DriverConfig(endpoint, database, credentials=credentials) def execute(config, query, params): with ydb.Driver(config) as driver: try: driver.wait(timeout=5, fail_fast=True) except Exception as e: print("Connect failed to YDB:", e) print(driver.discovery_debug_details()) raise session = driver.table_client.session().create() prepared_query = session.prepare(query) return session.transaction(ydb.SerializableReadWrite()).execute( prepared_query, params, commit_tx=True ) def insert_link(id, link): config = get_config() query = """ DECLARE $id AS Utf8; DECLARE $link AS Utf8; UPSERT INTO links (id, link) VALUES ($id, $link); """ params = {'$id': id, '$link': link} execute(config, query, params) def find_link(id): config = get_config() query = """ DECLARE $id AS Utf8; SELECT link FROM links where id=$id; """ params = {'$id': id} result_set = execute(config, query, params) if not result_set or not result_set[0].rows: return None # Учитываем структуру результата от ydb return result_set[0].rows[0].link def shorten(event): try: body = event.get('body') if body is None: return response(400, {'Content-Type': 'application/json'}, False, {'error': 'В теле запроса отсутствует тело'}) body = decode(event, body) # Попробуем распарсить JSON с ключом url, иначе считаем body как plain string url_value = None try: parsed = json.loads(body) if isinstance(parsed, dict): url_value = parsed.get('url') else: # если отправили не-объект JSON, игнорируем url_value = None except Exception: # body не JSON — считаем, что это plain URL url_value = body if not url_value: return response(400, {'Content-Type': 'application/json'}, False, {'error': 'Ожидался параметр url в теле запроса'}) # Очищаем URL от эвентуальных кодированных символов clean_url = urllib.parse.unquote(url_value).strip() if not clean_url: return response(400, {'Content-Type': 'application/json'}, False, {'error': 'Пустой url'}) link_id = hashlib.sha256(clean_url.encode('utf8')).hexdigest()[:6] insert_link(link_id, clean_url) # Возвращаем относительный путь — фронт сам допишет origin return response(200, {'Content-Type': 'application/json'}, False, {'url': f'/r/{link_id}'}) except Exception as e: print("Exception in shorten():", e) traceback.print_exc() return response(500, {'Content-Type': 'application/json'}, False, {'error': 'internal server error'}) def redirect(event): try: # защитно доставать path params path_params = event.get('pathParams') or event.get('pathParameters') or {} link_id = path_params.get('id') if not link_id: # возможно пришёл полный путь в event['url'] или ['path'] url = event.get('url') or event.get('path') or '' if url and url.startswith('/r/'): link_id = url.split('/r/')[-1].split('?')[0] if not link_id: return response(400, {'Content-Type': 'application/json'}, False, {'error': 'id отсутствует'}) redirect_to = find_link(link_id) if redirect_to: return response(302, {'Location': redirect_to}, False, '') return response(404, {'Content-Type': 'application/json'}, False, {'error': 'Данной ссылки не существует'}) except Exception as e: print("Exception in redirect():", e) traceback.print_exc() return response(500, {'Content-Type': 'application/json'}, False, {'error': 'internal server error'}) def get_result(url, event): if url == "/shorten" or url.startswith("/shorten"): return shorten(event) if url.startswith("/r/"): return redirect(event) return response(404, {'Content-Type': 'application/json'}, False, {'error': 'Данного пути не существует'}) def handler(event, context): url = event.get('url') or event.get('path') or '' if url: # Иногда URL из шлюза приходит с вопросительным знаком на конце if url.endswith('?'): url = url[:-1] return get_result(url, event) return response(404, {'Content-Type': 'application/json'}, False, {'error': 'Функция должна вызываться через API Gateway.'}) -
В блоке Код функции создайте файл
requirements.txtи вставьте в него следующий текст:ydb -
Укажите точку входа
index.handler. -
Задайте таймаут
5. -
Выберите сервисный аккаунт
serverless-shortener. -
Добавьте переменные окружения:
endpoint— введите первую часть сохраненного ранее поля Эндпоинт (часть до вхождения/?database=). Например,grpcs://ydb.serverless.yandexcloud.net:2135.database— введите вторую часть сохраненного ранее поля Эндпоинт (часть после вхождения/?database=). Например,/ru-central1/r1gra875baom********/g5n22e7ejfr1********.
-
Нажмите кнопку Сохранить изменения.
-
В блоке Обзор включите опцию Публичная функция.
Сохраните идентификатор созданной функции, он понадобится на следующих шагах.
Опубликуйте сервис через API Gateway
Чтобы опубликовать сервис через API Gateway:
-
Перейдите в свой рабочий каталог.
-
В списке сервисов выберите API Gateway.
-
Нажмите кнопку Создать API-шлюз.
-
В поле Имя введите
for-serverless-shortener. -
Скопируйте и вставьте следующий код в блок Спецификация:
Спецификация API-шлюза
openapi: 3.0.0 info: title: for-serverless-shortener version: 1.0.0 paths: /: get: x-yc-apigateway-integration: type: object_storage bucket: <имя_бакета> # <-- имя бакета object: index.html # <-- имя html-файла presigned_redirect: false service_account: <service_account_id> # <-- идентификатор сервисного аккаунта operationId: static /shorten: post: x-yc-apigateway-integration: type: cloud_functions function_id: <function_id> # <-- идентификатор функции operationId: shorten /r/{id}: get: x-yc-apigateway-integration: type: cloud_functions function_id: <function_id> # <-- идентификатор функции operationId: redirect parameters: - description: id of the url explode: false in: path name: id required: true schema: type: string style: simpleВнесите правки в код спецификации:
- Замените
<service_account_id>на идентификатор созданного ранее сервисного аккаунта. - Замените
<function_id>на идентификатор созданной ранее функции. - Замените
<имя_бакета>на имя созданного ранее бакета.
- Замените
-
Нажмите кнопку Создать.
-
Нажмите на имя созданного API-шлюза.
-
Скопируйте значение
urlиз спецификации.Используйте этот URL, чтобы проверить работу сокращателя ссылок.
Проверьте работу сокращателя
Чтобы проверить правильность взаимодействия компонентов сервиса:
-
Откройте в браузере скопированный ранее URL сокращателя.
-
В поле для ввода введите ссылку, которую вы хотите сократить.
-
Нажмите кнопку Сократить.
Ниже отобразится сокращенная ссылка.
-
Перейдите по сокращенной ссылке — должна открыться та же страница, что и по ссылке до сокращения.
Как удалить созданные ресурсы
Чтобы перестать платить за созданные ресурсы:
- Удалите API-шлюз
for-serverless-shortener. - Удалите функцию
for-serverless-shortener. - Удалите базу данных YDB
for-serverless-shortener. - Удалите бакет.