Автор оригинала: FreeCodeCamp Community Member.
Даниэль Ирсон
На протяжении всего этого поста мы будем создавать неверный URL-адрес URL, используя Amazon Web Services (AWS) Lambda и S3. Пока вам не требуют какого-либо предыдущего опыта с AWS, я предполагаю, что некоторые знакомы с ES6 JavaScript и Node.js.
По иронии судьбы, URL-адреса, которые будут сгенерированы из нашего коробки URL-адреса, часто будут дольше, чем URL-адреса, которые они перенаправляют – это потому, что мы используем адрес веб-сайта S3 по умолчанию. К концу поста обсудим, как вы можете добавить пользовательский домен, чтобы обойти это ограничение.
Посмотреть демонстрацию
Смотрите код на Github
Это относительно легко начать с AWS, и все же есть определенно воспринимаемая сложность. Количество доступных услуг может быть сложно выбрать от того, как многие из них перекрываются в функциональности. Медленная и неразрушающаяся консоль управления AWS не помогает, а также в текстовой онлайн-документации. Но на протяжении всего этого поста я надеюсь продемонстрировать, что лучший способ усыновить услуги AWS – использовать инкрементный подход, и вы можете начать работу, используя только несколько услуг.
Мы будем использовать Смертная рамка Чтобы взаимодействовать с AWS, и поэтому нет необходимости войти в консоль управления AWS. Serverless Framework предоставляет абстракцию AWS и помогает предоставить структуру проекта и разумные настройки по умолчанию. Если вы хотите узнать больше, прежде чем начать, вы должны прочитать их Документы Отказ
Архитектура
Прежде чем прыгать в любое развитие, давайте сначала посмотрим на службы AWS, мы будем использовать, чтобы построить наш URL-норм.
Для размещения нашего веб-сайта мы будем использовать службу хранения файлов Amazon S3. Мы настроим нашему ведру S3, которое можно рассматривать как папку верхнего уровня, чтобы обслуживать статический веб-сайт. Веб-сайт будет состоять из статического контента и сценариев с клиентами. Там нет возможности выполнить Server-Side Code (например, например PHP, Ruby или Java), но это нормально для нашего использования.
Мы также будем использовать малоизвестная особенность Из S3, что позволяет вам настроить пересылку для объектов внутри ведра S3, просто добавив Сайт-перенаправить-местоположение значение для метаданных объекта. Установка этого к URL-адресу будет браузеры, перенаправленные через ответ HTTP 301 и Расположение заголовок
URL-адрес объекта S3 состоит из адреса ведра S3, за которым следует имя объекта.
http://[bucket-name].s3-website-eu-west-1.amazonaws.com/[object-name]
Ниже приведен пример формата объекта ведра S3 для ЕС-Запад-1 область.
http://serverless-url-shortener.s3-website-eu-west-1.amazonaws.com/6GpLcdl
Название объекта «6GPLCDL» в конце URL в приведенном выше примере становится шорткодом для наших сокращенных URL. Используя эту функцию, мы получаем собственное перенаправление URL, а также возможности хранения. Мы не требуем базы данных для хранения деталей того, какие точки шортки, к которым указывается URL, поскольку эта информация вместо этого будет храниться с самим объектом.
Мы создадим функцию лямбда для сохранения этих объектов S3 с соответствующими метаданными к нашему ведрю S3.
В качестве альтернативы вы можете использовать клиентскую сторону AWS SDK в браузере для сохранения объектов. Но лучше извлечь эту функциональность в отдельный сервис. Он обеспечивает преимущество не о том, чтобы беспокоиться о разоблачении учетных данных о безопасности и более распространена в будущем. Мы рассмотрим функцию лямбда на конечную точку на Gateway API, так что это доступно через вызов API.
Начиная
Голова на Бесплатные рамки Words и пройти через их краткое руководство. В рамках процесса настройки вам придется установить AWS CLI и настройте учетные данные AWS.
Начните с создания Package.json файл в корне проекта.
{ "name": "serverless-url-shortener", "scripts": {}, "dependencies": {}}Мы знаем, что нам нужно использовать AWS SDK Поэтому идите вперед и установите его из NPM сейчас, введя следующую команду.
NPM установить AWS-SDK –Save
Теперь создайте config.json Файл также в корне проекта. Мы будем использовать это для хранения настраиваемых пользовательских опций в формате JSON.
Добавьте следующие ключи со значениями, соответствующими вашу настройку.
- Ведро – имя, которое вы хотите использовать для вашего ведра S3. Это станет частью короткого URL, если вы решите не добавлять пользовательский домен. Он должен быть уникален для региона, которого вы развертываете, чтобы не выбирать что-то слишком общее. Но не волнуйтесь, если ваше выбранное имя ведро уже используется, вы будете предупреждены через Serverless CLI в развертывании.
- Регион – AWS Область Вы хотите развернуть. Лучше всего подбирать регион, ближайший к своим пользователям по причинам эффективности. Если вы просто следуете наряду с учебником, я буду использовать
ЕС-Запад-1Отказ - Этап – сцена для развертывания. Как правило, у вас будет промежуточная среда, которая репливирует ту же конфигурацию, что и ваша производственная среда. Это позволяет вам неразрушают выбросы программного обеспечения. Как это учебник, я буду развертывать на
devсцена.
Ваш config.json Файл должен выглядеть аналогично следующему после завершения.
{ "BUCKET": "your-bucket-name", "REGION": "eu-west-1", "STAGE": "dev",}Далее создайте другой файл в корне проекта, Serverless.yml Отказ Это будет проводить нашу конфигурацию Framework без сердца, отформатированную на языке маркировки YAML.
Внутри этого файла мы начнем с определения нашей среды. Обратите внимание, как мы можем ссылаться на переменные, хранящиеся ранее в config.json Отказ
service: serverless-url-shortenerprovider: name: aws runtime: nodejs6.10 stage: ${file(config.json):STAGE} region: ${file(config.json):REGION} iamRoleStatements: - Effect: Allow Action: - s3:PutObject Resource: "arn:aws:s3:::${file(config.json):BUCKET}/*" iamrolestatements Секция относится к Личность и управление доступом который используется для настройки разрешений лямбда. Здесь мы даем лямбда-доступ к записи в наше ведро S3.
Чтобы сохранить объекты, нам нужно разрешение на выполнение S3: PutObject действие. Другие разрешения могут быть добавлены здесь, если они требуются в вашем проекте. Обратитесь к Docs S3 для других доступных действий.
Ресурс Значение установлено на ведро S3 Имя ресурса Amazon , который используется для однозначного идентификации конкретного ресурса AWS. Формат этого идентификатора зависит от службы AWS, который называется, но обычно у них есть следующий формат.
arn:partition:service:region:account-id:resource
Под провайдер Добавьте нашу Функции Конфигурация.
functions: store: handler: api.handle events: - http: path: / method: post cors: true
Здесь мы определяем конфигурацию API и сопоставьте нашу лямбду на событие http Post на базовом URL API. А обработчик со значением API.Handle относится к функции, названной ручка который экспортируется из API.JS (Нам не нужны JS Расширение файла, потому что ранее в Serverless.yml Мы устанавливаем время выполнения для Nodejs6.10 ).
Лямбда – это основанное на событии, и поэтому функции выполняются только на основе предопределенных триггеров. Здесь мы определили HTTP-мероприятие, но это могло бы также было событие, накопленное на таблице DynamOdb или очередь SQS.
Далее в Serverless.yml Мы определяем ресурсы AWS, которые будут созданы для нас на развертывание, используя Облачность Отказ Стоит отметить, что вы не обязательно должны настроить ресурсы таким образом, вы также можете создать их с помощью консоли управления AWS. Предоставление правильных разрешений доступа на месте неважно, как создаются ресурсы. Но в определении необходимых услуг в Serverless.yml Вы определяете свою «инфраструктуру как код» и получить ряд преимуществ при этом.
Продолжай и добавьте Ресурсы Конфигурация.
resources: Resources: ServerlessRedirectS3Bucket: Type: AWS::S3::Bucket Properties: BucketName: ${file(config.json):BUCKET} AccessControl: PublicRead WebsiteConfiguration: IndexDocument: index.html ServerlessRedirectS3BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: ${file(config.json):BUCKET} PolicyDocument: Statement: - Action: - s3:GetObject Effect: Allow Resource: - arn:aws:s3:::${file(config.json):BUCKET}/* Principal: "*"Мы просим ресурс ведра S3, сконфигурированный для использования статического хостинга сайта с index.html как корневой документ. Ведра S3 для веских причинами являются частными по умолчанию, поэтому нам нужно создать политику S3 Bucket, которая позволяет доступом к ней. Без этого политического сайта посетители сайта вместо этого показано неаутентическое сообщение об ошибке.
Создание API
Наша функция лямбда отвечает за четыре задания.
- Захватывающий URL сократить от представления формы пользователя.
- Генерируя уникальный шорткод для URL.
- Сохранение соответствующего объекта перенаправления к S3.
- Возвращая путь объекта к клиенту.
Создать обработчик
Создайте новый файл под названием API.JS и экспортировать функцию стрелки с именем ручка который занимает три аргумента: событие , контекст и Обратный вызов Отказ Они будут предоставлены AWS, когда обработчик вызывается. Этот файл является сценарием Node.js и для экспорта функции стрелки вам нужно добавить ее на Module.exports Отказ
module.exports.handle = (event, context, callback) => {}
Этот обработчик будет вызывать, когда на нашей конечной точке выполнен запрос на почту HTTP. Чтобы вернуть ответ API, нам нужно использовать прилагаемую функцию обратного вызова, предоставляемую в качестве третьего аргумента функции стрелки. Это Ошибка – первый обратный вызов который принимает два аргумента. Если запрос успешно завершен null следует передавать как первый аргумент. Объект отклика, прошедший в качестве второго аргумента, определяет тип ответа, который будет возвращен пользователю. Создание ответа так же просто, как обеспечивает СТАТУС КОДЫ и Тело Как показано в примере ниже.
const response = { statusCode: 201, body: JSON.stringify({ "shortUrl": "http://example.com" })}callback(null, response)
контекст Объект, прошедший в качестве второго аргумента к обработчику, содержит информацию выполнения, которая для этого учебника нам не нужен доступ. Однако мы должны использовать событие Передано в качестве первого аргумента, поскольку это содержит представление формы с URL для сокращения.
Разбирать запрос
Ниже приведен пример события шлюза API, который будет передан на наш обработчик, когда пользователь делает представление формы. Поскольку мы создаем наш URL-подросток как приложение одной страницы, мы отправляем форму, используя JavaScript и, следовательно, тип содержимого будет Приложение/JSON а не Приложение/X-WWW-Form-urlencoded Отказ
{ resource:'/', path:'/', httpMethod:'POST', headers: { Accept:'*/*', 'Accept-Encoding':'gzip, deflate', 'cache-control':'no-cache', 'CloudFront-Forwarded-Proto':'https', 'CloudFront-Is-Desktop-Viewer':'true', 'CloudFront-Is-Mobile-Viewer':'false', 'CloudFront-Is-SmartTV-Viewer':'false', 'CloudFront-Is-Tablet-Viewer':'false', 'CloudFront-Viewer-Country':'GB', 'content-type':'application/json', Host:'', 'User-Agent':'', 'X-Amz-Cf-Id':'', 'X-Amzn-Trace-Id':'', 'X-Forwarded-For':'', 'X-Forwarded-Port':'443', 'X-Forwarded-Proto':'https' }, queryStringParameters:null, pathParameters:{}, stageVariables:null, requestContext: { path:'/dev', accountId:'', resourceId:'', stage:'dev', requestId:'', identity:{ cognitoIdentityPoolId:null, accountId:null, cognitoIdentityId:null, caller:null, apiKey:'', sourceIp:'', accessKey:null, cognitoAuthenticationType:null, cognitoAuthenticationProvider:null, userArn:null, userAgent:'', user:null }, resourcePath:'/', httpMethod:'POST', apiId:'' }, body:'{"url":"http://example.com"}', isBase64Encoded:false}Нам нужна только представление формы с мероприятия, которое мы можем получить, посмотрев на запрос Тело Отказ Тело запроса сохраняется в виде строгого объекта JavaScript, который мы можем захватить внутри нашего обработчика, используя Json.parse () Отказ Воспользоваться Оценка короткого замыкания JavaScript Мы можем установить значение по умолчанию пустую строку для случаев, когда URL не был отправлен в рамках представления формы. Это позволяет нам лечить экземпляры, когда URL отсутствует, и где URL является пустой строкой одинаково.
module.exports.handle = (event, context, callback) => { let longUrl = JSON.parse(event.body).url || ''}Проверьте URL
Добавим некоторую базовую проверку, чтобы убедиться, что предоставленный URL выглядит законным. Есть несколько подходов, которые могут быть приняты для достижения этого. Но с целью этого учебника мы будем держать его простым и использовать встроенный Node.js URL модуль Отказ Мы построим нашу проверку, чтобы вернуть разрешенное обещание на действительный URL и вернуть отклоненное обещание на неверный URL. Обещания в JavaScript могут быть последовательно привязаны, чтобы разрешение одного обещания передано в обработчик успеха следующего. Мы будем использовать этот атрибут обещаний, чтобы структурировать наш обработчик. Давайте напишем функцию подтверждения обещаний.
const url = require('url')function validate (longUrl) { if (longUrl === '') { return Promise.reject({ statusCode: 400, message: 'URL is required' }) }let parsedUrl = url.parse(longUrl) if (parsedUrl.protocol === null || parsedUrl.host === null) { return Promise.reject({ statusCode: 400, message: 'URL is invalid' }) }return Promise.resolve(longUrl)}
В нашем проверить Функция мы сначала проверяем, что URL не установлен в пустую строку. Если мы возвращаем отклоненное обещание. Обратите внимание, как отклоненное значение является объектом, содержащим код состояния и сообщение. Мы будем использовать это позже, чтобы построить соответствующий ответ API. Призыв Разбор на Node.js URL Модуль возвращает объект URL с информацией, которая может быть извлечена из URL, который был передан в качестве строкового аргумента. В рамках нашей основной проверки URL-адресов мы просто проверяем, можно ли извлечь протокол (например, «http») и хост (например, «example.com»). Если любой из этих значений является null На возвращенном объекте URL мы предполагаем, что URL недействителен. Если URL-адрес действителен, мы возвращаем его как часть разрешенного обещания.
Возвращение ответа
После захвата URL-адреса от запроса мы называем проверить И для каждого дополнительного шага обработчика, который требуется, мы вернем новое обещание в обработке успеха предыдущего обещания. Обратный обработчик окончательного успеха отвечает за возврат ответа API через аргумент обратного вызова ручки. Он будет вызываться как для ответов API ошибок, создаваемых от отклоненных обещаний, а также успешных ответов API.
module.exports.handle = (event, context, callback) => { let longUrl = JSON.parse(event.body).url || '' validate(longUrl) .then(function(path) { let response = buildResponse(200, 'success', path) return Promise.resolve(response) }) .catch(function(err) { let response = buildResponse(err.statusCode, err.message) return Promise.resolve(response) }) .then(function(response) { callback(null, response) })}function buildResponse (statusCode, message, path = false) { let body = { message } if (path) body['path'] = path return { headers: { 'Access-Control-Allow-Origin': '*' }, statusCode: statusCode, body: JSON.stringify(body) }}Создать шорткод URL
API должен иметь возможность генерировать уникальные короткожки URL, которые представлены как имена файлов в ведре S3. В качестве шкала – это просто имя файла, существует большая степень гибкости в том, как она состоит. Для нашего Shortcode мы будем использовать 7-значную буквенно-цифровую строку, состоящую из заглавных и строчных букв, это означает 62 возможных комбинаций для каждого символа. Мы будем использовать рекурсию, чтобы создать шорткод, выбрав один символ за раз, пока семь не будут выбраны.
function generatePath (path = '') { let characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' let position = Math.floor(Math.random() * characters.length) let character = characters.charAt(position)if (path.length === 7) { return path}return generatePath(path + character)}
Хотя вероятность случайным образом генерации одного и того же шорты Slim (на самом деле есть 0,00000000000000000000000000000000000000000000000000000080000000000000000000000008063360000000000000080633655516 Есть Headobject Способ на службе S3, который загружает метаданные объекта. Мы можем использовать это, чтобы проверить, существует ли объект с тем же именем, что и когда объект не найден обещание с кодом Незначительно отклонен. Это отклоненное обещание указывает на то, что шорткод свободен и может быть использован. Призыв Headobject более исполнен, чем тестирование, существует ли объект через GetObject , который загружает весь объект.
const AWS = require('aws-sdk')const S3 = new AWS.S3()function isPathFree (path) { return S3.headObject(buildRedirect(path)).promise() .then(() => Promise.resolve(false)) .catch(function (err) { if (err.code == 'NotFound') { return Promise.resolve(true) } else { return Promise.reject(err) } })}function buildRedirect (path, longUrl = false) { let redirect = { 'Bucket': config.BUCKET, 'Key': path }if (longUrl) { redirect['WebsiteRedirectLocation'] = longUrl }return redirect}
Мы можем использовать Подвеска рекурсивно найти уникальный путь объекта.
function getPath () { return new Promise(function (resolve, reject) { let path = generatePath() isPathFree(path) .then(function (isFree) { return isFree ? resolve(path) : resolve(getPath()) }) })}Воспользовавшись способностью цепи обещания, мы вернем новый вызов GetPath Если Подвеска Возвращает false.
Чтобы сохранить объект после того, как уникальный шток был найден, нам просто нужно позвонить в PutObject Способ на сервисе AWS SDK S3. Давайте завернум это в функции, которая разрешает шорткод, если PutObject Вызов метода был успешным и возвращает объект ошибок для создания ответа API, если это не так.
function saveRedirect (redirect) { return S3.putObject(redirect).promise() .then(() => Promise.resolve(redirect['Key'])) .catch(() => Promise.reject({ statusCode: 500, message: 'Error saving redirect' })}Используя вышеуказанные функции, мы можем добавить два новых обработчика успеха обещания для доработки конечной точки API. Нам нужно вернуть GetPath От первого обработчика успеха обещания, который будет разрешать уникальный шток URL-адрес. Возвращение Совесение С помощью объекта Redirect, построенный с помощью этого уникального шкала во втором обработчике успеха, сохранит объект к ведрю S3. Затем этот объект может быть возвращен клиенту как часть ответа API. Теперь наш обработчик должен быть завершен.
module.exports.handle = (event, context, callback) => { let longUrl = JSON.parse(event.body).url || '' validate(longUrl) .then(function () { return getPath() }) .then(function (path) { let redirect = buildRedirect(path, longUrl) return saveRedirect(redirect) }) .then(function (path) { let response = buildResponse(200, 'success', path) return Promise.resolve(response) }) .catch(function (err) { let response = buildResponse(err.statusCode, err.message) return Promise.resolve(response) }) .then(function (response) { callback(null, response) })}Разверните API
Беги Смертное развертывание В вашем терминале развернуть API к AWS. Это будет настроить наше ведро S3 и вернуть URL конечной точки. Держите URL-карту конечной точки Handy, поскольку нам понадобится позже.
Serverless: Packaging service...Serverless: Excluding development dependencies...Serverless: Uploading CloudFormation file to S3...Serverless: Uploading artifacts...Serverless: Uploading service .zip file to S3 (5.44 MB)...Serverless: Validating template...Serverless: Updating Stack...Serverless: Checking Stack update progress.................Serverless: Stack update finished...Service Informationservice: serverless-url-shortenerstage: devregion: eu-west-1stack: serverless-url-shortener-devapi keys: Noneendpoints: POST - https://t2fgbcl26h.execute-api.eu-west-1.amazonaws.com/dev/functions: store: serverless-url-shortener-dev-storeServerless: Removing old service versions...
Создание интерфейса
Чтобы помочь с дизайном Frontend, мы будем использовать Picartcsss фреймворк. Мы также будем захватывать jquery Чтобы упростить работу с DOM и делать запросы AJAX. Стоит отметить, что для производственной среды вы, вероятно, захотете тянуть две легкие зависимости, но, поскольку это просто учебное пособие, я чувствую, что это приемлемо.
Создать Статический Папка, поэтому у нас есть где-то хранить наш интернет-код.
Скачать зависимости
Сохранить копию Paper.min.css и jquery-3.2.1.min.js нашим недавно созданным Статический Папка, они являются полезными версиями Framework and jQuery Library соответственно.
Добавьте HTML
Создайте новый файл под названием index.html внутри Статический Папка и добавьте необходимую HTML. Нам нужна форма с URL-входом и кнопкой для отправки формы. Нам также нужно где-то ставить результат любых вызовов API, что для успешного вызова API будет укороченным URL-адресом, а для неудачного вызова API это будет сообщение об ошибке.
Serverless url shortener Serverless url shortener
Хотя не показано в блоке кода выше для краткости, убедитесь, что вы устанавливаете действие формы на конечную точку API, которая была отображена при запуске Смертное развертывание Отказ Если у вас больше не получится доступа к вашему терминальному выходу из этого развертывания, вы можете узнать URL конечной точки через Безвесовая информация команда.
Сделать запросы API
Перед записью JavaScript сделать запросы на нашу API, давайте сначала загрузим jQuery, добавив тег скрипта как раз перед dy> и ссылается на обозначенный файл, который мы загрузили ранее.
Теперь добавьте другую пару меток скрипта под ними и внутри Давайте создадим функцию, которую можно использовать для отображения сообщения пользователям, используя сообщение Div В нашем шаблоне, который установлен на Дисплей: Нет По умолчанию на странице нагрузки. Чтобы показать сообщение, мы можем просто установить текст внутри этого Div Использование Текст () и переключите дисплей, используя Показать () Отказ
Давайте напишем еще одну функцию, чтобы перейти в тот же набор тегов сценариев, которые будут использовать jQuery, чтобы сделать запросы на нашу API.
function shortenLink (apiUrl, longUrl) { $.ajax(apiUrl, { type : 'POST', data: JSON.stringify({url: longUrl})}) .done(function (responseJSON) { var protocol = window.location.protocol + '//' var host = window.location.host + '/' var shortUrl = protocol + host + responseJSON.path addMessage(shortUrl) }) .fail(function (data) { if (data.status === 400) { addMessage(data.responseJSON.message) } else { addMessage('an unexpected error occurred') } })}Эта функция создает запрос на пост и устанавливает корпус запроса на объект JSON, содержащий URL для сокращения. Если запрос завершится успешно, и был возвращен код состояния HTTP 2XX, он захватывает шорткод из путь Ключ в ответе и создает полностью квалифицированный короткий URL для представления пользователю, используя addmessage Функция создана ранее. Если запрос был неудачным, то отображается сообщение об ошибке.
Наконец мы можем подключить это к нашей форме, добавив на обработчик отправки. Мы получаем URL API EndPoint URL из атрибута Action Form и получите URL для сокращения от URL Форма ввода.
$('form').submit(function (event) { event.preventDefault() addMessage('...') shortenLink(event.target.action, event.target.url.value)})Разверните сайт
Для развертывания веб-сайта мы будем использовать AWS CLI Синхронизация Команда для загрузки содержимого статической папки в наше ведро S3. Беги AWS S3 Sync Static S3://[Ведро] В вашем терминале замена [Ведро] С выбранным именем вашего ведра config.json Отказ После того, как это завершится, вы должны быть в состоянии отправиться к вашему адресу ведра S3 в браузере, чтобы увидеть короткоформат URL в действии. Общественные URL для ведер S3 принимают следующую форму.
http://[bucket].s3-website-[region].amazonaws.com
Таким образом, после добавления имени вашего ведра и регион вашего адреса записи URL-адреса должен выглядеть аналогично ниже.
http://serverless-url-shortener.s3-website-eu-west-1.amazonaws.com
Чтобы добавить пользовательский домен в ваше ведро, вы должны следовать одному из инструкций в Этот AWS поддерживает статью Отказ Для самого простого варианта вы должны установить имя ведра для вашего домена www поддомен (например, www.example.com). Если вы затем добавьте запись CNAME в конфигурации DNS для www Поддодомен и установите его на ваш адрес S3 Bucket, веб-сайт должен быть доступен через ваш домен. Обязательно удалите все существующие А Записи и имейте в виду, что это не настроит перенаправление из вашего корневого домена в www субоман. Там есть пара способов, которыми это можно решить, которые описаны в статье AWS.
Заворачивать
Я надеюсь, что вы нашли этот учебник полезным. Реальность такова, что AWS невероятно гибкий, и в этой статье мы прошли только один из способов создания коробки URL, используя лямбда и s3. Но есть множество других способов того, что тот же процесс также может быть достигнут.
Если вы нашли это интересно, вы можете наслаждаться Один из моих предыдущих статей Где я создал форму экспедирования формы, используя AWS Lambda.
Представляем FormPlug V1, сервис переадресации формы для AWS Lambda Предполагается, что примерно 269 миллиардов электронных писем отправляются за один день. Более 10 миллионов были отправлены, как вы читаете ... Hackernoon.com.