Когда речь заходит о Node.js-фреймворках для серьёзных приложений, разработчики обычно вспоминают Express и его минималистичный подход. Но что делать, когда нужна не просто библиотека для маршрутизации, а полноценная архитектура с моделями, контроллерами, ORM и WebSocket-поддержкой из коробки? Тут на сцену выходит Sails.js - фреймворк, который многие называют ответом мира JavaScript на Ruby on Rails. Идея простая. Взять успешные паттерны из рельсов, переписать на Node, добавить поддержку реалтайм-коммуникаций и предоставить разработчику готовый каркас, в котором решения уже приняты за него.
Sails.js появился в 2012 году и за десять лет успел обрасти приличным сообществом, документацией и набором приложений в продакшене. Главная фишка фреймворка - встроенная ORM под названием Waterline, которая умеет работать с десятком разных хранилищ от MySQL и PostgreSQL до MongoDB и Redis. Это значит, что код приложения остаётся одинаковым независимо от выбора базы. Поменял адаптер - и Waterline сама подстроится под особенности конкретной СУБД.
В материале разбирается полный цикл развёртывания Sails-приложения на сервере Ubuntu 22.04. От настройки брандмауэра и установки правильной версии Node.js через NVM до создания systemd-юнита, проксирования через Nginx и подключения бесплатного SSL-сертификата от Let's Encrypt. На выходе получится готовая боевая конфигурация, которую не стыдно показать в продакшене.
Подготовка сервера и открытие нужных портов на брандмауэре UFW для будущего веб-приложения
Перед началом любых установок система должна быть в актуальном состоянии. Свежие обновления безопасности, последние версии библиотек, никаких подвисших процессов от предыдущих экспериментов. Для этого выполняется стандартная пара команд.
$ sudo apt update && sudo apt upgrade
Менеджер пакетов сначала обновит индекс репозиториев, а затем подтянет все доступные обновления для установленных пакетов. На свежей системе процесс занимает пару минут, на запущенной может растянуться и до получаса. Терпение тут полезнее спешки.
Дальше нужно убедиться, что брандмауэр UFW работает и пропускает только то, что положено. Этот инструмент входит в стандартную поставку Ubuntu и представляет собой удобную обёртку вокруг iptables. Проверка текущего состояния выполняется одной строкой.
$ sudo ufw status
На свежем сервере вывод обычно выглядит так - открыт только SSH, всё остальное закрыто наглухо.
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
Веб-приложение понадобится отдавать пользователям через стандартные порты HTTP и HTTPS, поэтому их нужно явно разрешить.
$ sudo ufw allow http
$ sudo ufw allow https
UFW понимает символьные имена сервисов, заглядывая в файл /etc/services для определения номеров портов. Под капотом эти команды откроют 80-й и 443-й TCP-порты для всех входящих соединений. Альтернативно можно было бы написать sudo ufw allow 80/tcp, результат был бы тот же. После повторной проверки статуса картина уже посимпатичнее.
$ sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
80/tcp ALLOW Anywhere
443 ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
80/tcp (v6) ALLOW Anywhere (v6)
443 (v6) ALLOW Anywhere (v6)
Брандмауэр готов пропускать веб-трафик. Никакая случайная служба, открывшая порт без спроса, наружу не пробьётся.
Установка менеджера версий Node.js под названием NVM для гибкого переключения между релизами платформы
Node.js развивается быстро, и привязываться к одной конкретной версии - решение недальновидное. Сегодня проект работает на Node 16, через год понадобится Node 20, а ещё через год выйдет какой-нибудь экспериментальный релиз с прорывными фичами. Менять глобальную версию каждый раз больно. Тут выручает Node Version Manager, или сокращённо NVM. Утилита позволяет держать на одной машине сколько угодно параллельных версий Node и переключаться между ними одной командой.
Свежие релизы NVM публикуются на странице релизов проекта в GitHub. Перед установкой имеет смысл заглянуть туда и зафиксировать актуальный номер версии в переменной.
$ NVMVERSION=0.39.1
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/$NVMVERSION/install.sh | bash
Первая строка просто запоминает номер версии в shell-переменной. Вторая скачивает официальный установочный скрипт и сразу прогоняет его через интерпретатор bash. Скрипт клонирует репозиторий NVM в каталог ~/.nvm, прописывает несколько строк инициализации в ~/.bashrc и настраивает переменные окружения.
Чтобы изменения в bashrc подхватились в текущем сеансе, файл нужно перечитать.
$ source ~/.bashrc
Без этой команды NVM формально установлен, но shell о нём ещё не знает - переменные окружения подгружаются только при старте новой сессии. Команда source перезагружает конфигурацию прямо сейчас, без необходимости перелогиниваться.
Финальная проверка - убедиться, что NVM действительно доступен.
$ command -v nvm
Если в ответ пришла строка nvm, всё в порядке. Утилита распознана как встроенная функция оболочки. Если же терминал молчит или ругается, что команда не найдена, стоит ещё раз пройтись по предыдущим шагам и проверить содержимое bashrc.
nvm
Установка LTS-версии Node.js через NVM и проверка работоспособности интерпретатора
Когда менеджер версий на месте, остаётся выбрать, какой именно Node устанавливать. Правильный выбор для большинства производственных задач - LTS-релиз, что расшифровывается как Long Term Support. Эти версии получают обновления безопасности и критические патчи в течение тридцати месяцев после выхода. Эксперименты с самыми свежими билдами лучше оставить для домашних проектов, на боевом сервере спокойнее с проверенным релизом.
$ nvm install --lts
NVM скачает архив с бинарниками Node.js последней LTS-версии, распакует его в каталог ~/.nvm/versions/node/, настроит переменные окружения и сделает только что установленную версию активной. Процесс занимает обычно меньше минуты на нормальном канале.
Проверка проходит классически.
$ node --version
v16.17.0
Конкретный номер версии может отличаться - LTS-линейка обновляется регулярно, и через полгода после выхода этого материала в строю будет уже Node 18 или 20. Главное, чтобы команда вообще что-то ответила без ошибок.
Глобальная установка Sails.js через npm и знакомство с командной строкой фреймворка
Sails.js поставляется как обычный npm-пакет. Установка глобальная, чтобы команда sails была доступна из любой директории - это удобно при создании новых проектов и работе с уже существующими.
$ npm -g install sails
Флаг -g (global) указывает npm разместить пакет не в локальном каталоге node_modules, а в системной директории, прописанной в конфигурации Node. Все исполняемые скрипты пакета попадают в каталог, добавленный в переменную PATH, и становятся вызываемыми по имени откуда угодно.
После установки имеет смысл убедиться, что фреймворк ставился без сюрпризов.
$ sails --version
1.5.3
У Sails есть несколько команд для разных сценариев работы - создание проекта, запуск, генерация контроллеров и моделей, миграции базы данных. Полный список выводится через флаг --help.
$ sails --help
Вывод довольно объёмный и охватывает основные операции с проектами.
Usage: sails [command]
Options:
-v, --version output the version number
-h, --help output usage information
Commands:
version
lift|l [options]
new [options] [path_to_new_app]
generate
upgrade
migrate
console|c [options]
www
debug (for Node v5 and below)
inspect (for Node v6 and above)
run
test
lint
deploy
debug-console|dc
help [command]
Команда lift запускает приложение в режиме разработки, new создаёт новый проект, generate помогает быстро накидать каркас контроллеров и моделей. Команда console открывает интерактивную REPL-оболочку с подключённым контекстом приложения - удобно для отладки запросов к базе или ручной проверки бизнес-логики.
Создание тестового приложения с предустановленным шаблоном аутентификации и его первый запуск
Sails.js предлагает два стартовых шаблона для нового проекта. Первый - полноценное веб-приложение с готовой системой регистрации, входа и восстановления пароля. Второй - чистый каркас без какой-либо логики, который собирается с нуля под конкретные нужды. Для знакомства с фреймворком первый вариант информативнее, потому что показывает, как устроены типовые компоненты Sails-проекта.
$ sails new howtoforge-app
Команда запросит выбор шаблона.
Choose a template for your new Sails app:
1. Web App · Extensible project with auth, login, & password recovery
2. Empty · An empty Sails app, yours to configure
(type "?" for help, or <CTRL+C> to cancel)
Выбор первого варианта запустит установку всех зависимостей, прописанных в шаблоне веб-приложения. На сервере с обычным каналом процесс займёт от двух до пяти минут. Для быстрого старта без ожидания можно использовать флаг --fast, но это полезно только тем, кто понимает, что именно собирается пропустить.
? 1
info: Installing dependencies...
Press CTRL+C to cancel.
(to skip this step in the future, use --fast)
info: Created a new Sails app `howtoforge-app`!
После завершения установки нужно перейти в каталог свежесозданного приложения.
$ cd howtoforge-app
Структура проекта раскрывается простым ls.
$ ls
total 944
drwxrwxr-x 9 navjot navjot 4096 Aug 23 07:14 .
drwxr-x--- 8 navjot navjot 4096 Aug 23 07:13 ..
drwxrwxr-x 8 navjot navjot 4096 Aug 23 07:13 api
-rw-rw-r-- 1 navjot navjot 1841 Aug 23 07:13 app.js
drwxrwxr-x 8 navjot navjot 4096 Aug 23 07:13 assets
drwxrwxr-x 4 navjot navjot 4096 Aug 23 07:13 config
-rw-rw-r-- 1 navjot navjot 1046 Aug 23 07:13 .editorconfig
-rw-rw-r-- 1 navjot navjot 44 Aug 23 07:13 .eslintignore
-rw-rw-r-- 1 navjot navjot 4228 Aug 23 07:13 .eslintrc
-rw-rw-r-- 1 navjot navjot 3531 Aug 23 07:13 .gitignore
-rw-rw-r-- 1 navjot navjot 669 Aug 23 07:13 Gruntfile.js
-rw-rw-r-- 1 navjot navjot 709 Aug 23 07:13 .htmlhintrc
-rw-rw-r-- 1 navjot navjot 2162 Aug 23 07:13 .lesshintrc
drwxrwxr-x 510 navjot navjot 20480 Aug 23 07:14 node_modules
-rw-rw-r-- 1 navjot navjot 369 Aug 23 07:13 .npmrc
-rw-rw-r-- 1 navjot navjot 6151 Aug 23 07:13 package.json
-rw-rw-r-- 1 navjot navjot 854958 Aug 23 07:14 package-lock.json
-rw-rw-r-- 1 navjot navjot 1732 Aug 23 07:13 README.md
-rw-rw-r-- 1 navjot navjot 123 Aug 23 07:13 .sailsrc
drwxrwxr-x 2 navjot navjot 4096 Aug 23 07:13 scripts
drwxrwxr-x 4 navjot navjot 4096 Aug 23 07:13 tasks
drwxrwxr-x 5 navjot navjot 4096 Aug 23 07:13 views
Каталог api содержит контроллеры, модели и хуки приложения. В config лежат конфигурационные файлы. Папка assets хранит фронтенд-ресурсы, которые автоматически собираются при изменениях. Views отвечает за серверные шаблоны. Точка входа - файл app.js в корне проекта.
Запуск приложения в режиме разработки выполняется командой lift.
$ sails lift
Sails использует инструмент Grunt для слежения за каталогом /assets - изменения в стилях или клиентских скриптах подхватываются автоматически без перезапуска сервера. Шаблоны представлений тоже обновляются на лету, потому что в режиме разработки кеширование отключено.
Удачный запуск выглядит примерно так.
info: Starting app...
info: Initializing project hook... (`api/hooks/custom/`)
info: Initializing `apianalytics` hook... (requests to monitored routes will be logged!)
info: ·• Auto-migrating... (alter)
info: Hold tight, this could take a moment.
info: ? Auto-migration complete.
debug: Running v0 bootstrap script... (looks like this is the first time the bootstrap has run on this computer)
info:
info: .-..-.
info:
info: Sails <| .-..-.
info: v1.5.3 |\
info: /|.\
info: / || \
info: ,' |' \
info: .-'.-==|/_--'
info: `--'-------'
info: __---___--___---___--___---___--___
info: ____---___--___---___--___---___--___-__
info:
info: Server lifted in `/home/navjot/howtoforge-app`
info: To shut down Sails, press <CTRL> + C at any time.
info: Read more at https://sailsjs.com/support.
debug: -------------------------------------------------------
debug: :: Tue Aug 23 2022 09:01:32 GMT+0000 (Coordinated Universal Time)
debug: Environment : development
debug: Port : 1337
debug: -------------------------------------------------------
По умолчанию Sails слушает порт 1337 - этот номер с самой первой версии стал визитной карточкой фреймворка. Чтобы добраться до приложения снаружи, нужно временно открыть порт в брандмауэре. Делается это во второй терминальной сессии, чтобы не прерывать работающий sails lift.
$ sudo ufw allow 1337
После этого по адресу http://<serverIP>:1337 в браузере покажется стартовая страница приложения. Завершить работу запущенного процесса можно сочетанием Ctrl + C в терминале с sails lift.
Настройка systemd-юнита для автоматического запуска приложения и обеспечение устойчивости после перезагрузок
Запуск приложения в терминале хорош для разработки, но никуда не годится для боевой среды. Стоит закрыть SSH-сессию или перезагрузить сервер, и приложение тут же ляжет. Решение - оформить запуск в виде systemd-сервиса, который умеет стартовать процесс при загрузке системы и перезапускать его при сбоях.
$ sudo nano /etc/systemd/system/howtoforge-app.service
Содержимое юнита выглядит так.
[Unit]
After=network.target
[Service]
Type=simple
User=navjot
WorkingDirectory=/home/navjot/howtoforge-app
ExecStart=/home/navjot/.nvm/versions/node/v16.17.0/bin/node app.js
Restart=on-failure
[Install]
WantedBy=multi-user.target
Разберём ключевые директивы. Секция Unit содержит общие настройки. Параметр After=network.target сообщает systemd, что сервис нужно запускать после того, как поднимется сетевая подсистема. Без этой строки приложение могло бы стартовать раньше, чем сеть, и упасть с ошибкой привязки к порту.
В секции Service самое интересное. Тип simple означает, что главный процесс - это тот, который запущен в ExecStart, никаких форков и демонизаций не предполагается. Параметр User задаёт, под каким системным пользователем будет работать приложение - запускать ноду от root противопоказано, любая дыра в коде станет дырой в системе. WorkingDirectory указывает рабочий каталог, относительно которого приложение будет искать свои файлы.
ExecStart содержит полную команду запуска. Здесь намеренно используется не sails lift, а прямой вызов node с файлом app.js. Причина банальна. Sails CLI - это удобная обёртка для разработки, но в продакшене проще управлять чистым процессом Node без посредников. Путь к самому интерпретатору тоже прописан полностью, потому что systemd не знает про NVM и переменные окружения текущего пользователя.
Найти точный путь к ноде помогает which.
$ which node
/home/navjot/.nvm/versions/node/v16.17.0/bin/node
Этот путь нужно подставить в ExecStart на своей системе. Версия 16.17.0 со временем сменится на более свежую, и юнит придётся править.
Параметр Restart=on-failure оживляет приложение, если оно упало с ненулевым кодом возврата. Полезная штука для серверов, где простой недопустим.
Секция Install с записью WantedBy=multi-user.target отвечает за автозапуск при загрузке системы - когда сервер выйдет в многопользовательский режим, systemd подтянет и наш сервис.
После сохранения файла нужно перечитать конфигурацию systemd, чтобы новый юнит был замечен.
$ sudo systemctl daemon-reload
Затем сервис запускается и одновременно прописывается в автозагрузку.
$ sudo systemctl enable howtoforge-app --now
Флаг --now означает не только активировать сервис в автозапуске, но и сразу его запустить. Без этого флага пришлось бы дополнительно делать systemctl start.
Проверка статуса покажет картину происходящего.
$ sudo systemctl status howtoforge-app
? howtoforge-app.service - Sails.js Howtoforge App
Loaded: loaded (/etc/systemd/system/howtoforge-app.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2022-08-23 11:52:58 UTC; 5s ago
Main PID: 15385 (node)
Tasks: 22 (limit: 2237)
Memory: 123.8M
CPU: 3.894s
CGroup: /system.slice/howtoforge-app.service
??15385 /home/navjot/.nvm/versions/node/v16.17.0/bin/node app.js
??15392 grunt "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ">
Aug 23 11:53:01 sails node[15385]: info: ____---___--___---___--___---___--___-__
Aug 23 11:53:01 sails node[15385]: info:
Aug 23 11:53:01 sails node[15385]: info: Server lifted in `/home/navjot/howtoforge-app`
Aug 23 11:53:01 sails node[15385]: info: To shut down Sails, press <CTRL> + C at any time.
Aug 23 11:53:01 sails node[15385]: info: Read more at https://sailsjs.com/support.
Aug 23 11:53:01 sails node[15385]: debug: -------------------------------------------------------
Aug 23 11:53:01 sails node[15385]: debug: :: Tue Aug 23 2022 11:53:01 GMT+0000 (Coordinated Universal Time)
Aug 23 11:53:01 sails node[15385]: debug: Environment : development
Aug 23 11:53:01 sails node[15385]: debug: Port : 1337
Aug 23 11:53:01 sails node[15385]: debug: -------------------------------------------------------
Зелёный active (running) - то, что нужно. Приложение живёт независимо от терминальной сессии и переживёт перезагрузку сервера.
Установка Nginx из официального репозитория и получение SSL-сертификата от Let's Encrypt через Certbot
Для боевого сайта нужен серьёзный фронт-сервер, а не порт 1337 наружу. Nginx подходит идеально - быстрый, надёжный, с богатыми возможностями по проксированию и работе с TLS. Версия из штатного репозитория Ubuntu 22.04 устаревает быстро, поэтому правильнее подключить официальный репозиторий разработчиков Nginx.
Сначала импортируется ключ для проверки подписи пакетов.
$ curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
Команда скачивает GPG-ключ Nginx, преобразует его из текстового ASCII-armored формата в бинарный через gpg --dearmor и сохраняет в системный каталог keyrings. Все последующие установки пакетов будут проверяться этим ключом - если кто-то подменит репозиторий, apt сразу заметит несовпадение подписи.
Затем подключается сам репозиторий.
$ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg arch=amd64] \
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
Здесь используется подстановка lsb_release -cs, которая возвращает кодовое имя текущего релиза Ubuntu - jammy для 22.04, focal для 20.04 и так далее. Параметр signed-by привязывает репозиторий к конкретному ключу из предыдущего шага. Это современный подход вместо устаревшей команды apt-key add.
Дальше обычное обновление индексов и установка.
$ sudo apt update
$ sudo apt install nginx
Проверка версии должна показать что-то посвежее, чем шипящие в дистрибутивных репозиториях.
$ nginx -v
nginx version: nginx/1.22.0
Теперь дело за SSL-сертификатом. Let's Encrypt раздаёт их бесплатно и автоматически, нужен лишь клиент. Самый удобный способ заполучить свежую версию Certbot - использовать Snap, потому что в нём всегда лежит последний релиз без задержек на пересборку дистрибутивных пакетов.
$ sudo snap install core
$ sudo snap refresh core
Эти две команды устанавливают и обновляют базовый snap-пакет core, на котором работают все остальные snap-приложения. На свежей Ubuntu 22.04 он обычно уже стоит, но проверка лишней не будет.
Сам Certbot ставится в режиме classic, который даёт пакету полный доступ к системе - стандартная sandbox-изоляция snap для него слишком тесная.
$ sudo snap install --classic certbot
Чтобы команду certbot можно было вызывать из любого места без полного пути, делается символическая ссылка в /usr/bin.
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
Запрос самого сертификата - ключевой момент. Параметры команды стоит разобрать подробно.
$ sudo certbot certonly --standalone --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. -d sails.example.com
Режим certonly означает только получение сертификата без автоматической настройки веб-сервера. Флаг --standalone запускает встроенный мини-сервер Certbot для прохождения проверки владения доменом - именно поэтому Nginx должен быть остановлен в момент получения сертификата. Параметр --agree-tos соглашается с пользовательским соглашением Let's Encrypt без интерактивного запроса. Флаг --no-eff-email отказывается от рассылки от Electronic Frontier Foundation. --staple-ocsp включает поддержку OCSP-степлинга. --preferred-challenges http выбирает HTTP-валидацию вместо DNS. После -m указывается контактный email, после -d - доменное имя.
Сертификат сохраняется в каталог /etc/letsencrypt/live/sails.example.com/ со всеми нужными файлами - fullchain.pem, privkey.pem, chain.pem, cert.pem.
Для усиления криптографии генерируется группа Диффи-Хеллмана с ключом 4096 бит.
$ sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096
Параметр -dsaparam ускоряет генерацию в десятки раз без существенной потери стойкости. Без него ожидание могло бы растянуться на час и больше.
Сертификаты Let's Encrypt живут только 90 дней, поэтому автоматическое продление - не роскошь, а необходимость. Файл renewal-конфигурации правится так, чтобы при обновлении Nginx корректно останавливался и стартовал.
$ sudo nano /etc/letsencrypt/renewal/sails.example.com.conf
В конец файла добавляются хуки.
pre_hook = systemctl stop nginx
post_hook = systemctl start nginx
Pre-hook выполняется перед обновлением, post-hook - после. Standalone-метод требует свободного 80-го порта, поэтому Nginx останавливается на время процедуры. Без этих хуков пришлось бы продлевать сертификат руками каждые два месяца.
Проверить, всё ли настроено правильно, помогает прогон в режиме симуляции.
$ sudo certbot renew --dry-run
Если ошибок нет, автообновление настроено корректно и в дальнейшем будет срабатывать через системный таймер без всякого внимания.
Конфигурация Nginx как обратного прокси для Sails-приложения с поддержкой HTTP/2 и TLS 1.3
Базовый конфиг Nginx нуждается в небольшой правке - размер хеш-таблицы для имён серверов по умолчанию маловат для длинных доменов.
$ sudo nano /etc/nginx/nginx.conf
Перед строкой include /etc/nginx/conf.d/*.conf; добавляется директива.
server_names_hash_bucket_size 64;
Дальше создаётся отдельный конфиг для приложения.
$ sudo nano /etc/nginx/conf.d/sails.conf
В файл вставляется содержательный конфиг с учётом всех современных требований по безопасности и производительности.
upstream backend {
server 127.0.0.1:1337;
keepalive 32;
}
server {
listen 80 default_server;
server_name sails.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name sails.example.com;
http2_push_preload on; # Enable HTTP/2 Server Push
ssl_certificate /etc/letsencrypt/live/sails.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sails.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/sails.example.com/chain.pem;
ssl_session_timeout 1d;
# Enable TLS versions (TLSv1.3 is required upcoming HTTP/3 QUIC).
ssl_protocols TLSv1.2 TLSv1.3;
# Enable TLSv1.3's 0-RTT. Use $ssl_early_data when reverse proxying to
# prevent replay attacks.
#
# @see: https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_early_data
ssl_early_data on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:50m;
# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
add_header X-Early-Data $tls1_3_early_data;
access_log /var/log/nginx/sails.access.log main;
error_log /var/log/nginx/sails.error.log;
location / {
client_max_body_size 10M;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_buffering off;
proxy_http_version 1.1;
proxy_pass http://backend;
}
}
# This block is useful for debugging TLS v1.3. Please feel free to remove this
# and use the `$ssl_early_data` variable exposed by NGINX directly should you
# wish to do so.
map $ssl_early_data $tls1_3_early_data {
"~." $ssl_early_data;
default "";
}
Конфиг плотный, в нём много полезного. Блок upstream определяет бэкенд-пул из одного сервера на локальном порту 1337 с поддержкой keepalive-соединений - 32 соединения держатся открытыми для повторного использования, что снижает накладные расходы на установку TCP-сессий.
Первый server-блок слушает 80-й порт и редиректит весь трафик на HTTPS-версию. Это стандартный паттерн принудительного шифрования.
Второй server-блок и есть основное действие. Он слушает 443-й порт с включённым SSL и HTTP/2. Поддерживаются только современные протоколы TLS 1.2 и 1.3, всё устаревшее отключено. Параметр ssl_early_data включает 0-RTT - механизм TLS 1.3, позволяющий клиенту отправить первый запрос вместе с самым первым пакетом подключения, без ожидания завершения handshake. Прирост скорости заметен особенно на мобильных сетях с большим RTT.
Список шифров жёстко фиксирован и содержит только проверенные алгоритмы с эфемерными ключами Диффи-Хеллмана на эллиптических кривых (ECDHE) - это даёт forward secrecy, то есть защиту от расшифровки старого трафика даже при компрометации приватного ключа.
OCSP-степлинг через ssl_stapling позволяет Nginx самому получать у Let's Encrypt подтверждение валидности сертификата и прикреплять его к ответам, избавляя клиентов от необходимости самостоятельно ходить за этой информацией.
Блок location проксирует все запросы на бэкенд. Ключевые заголовки proxy_set_header передают информацию об оригинальном клиенте - реальный IP, протокол, имя хоста. Без них приложение не сможет понять, что работает за прокси, и будет видеть все запросы как идущие с 127.0.0.1. Заголовки Upgrade и Connection нужны для корректной работы WebSocket - Sails активно использует их для realtime-коммуникаций. Параметр client_max_body_size 10M ограничивает размер загружаемых файлов десятью мегабайтами, цифру меняют под свои нужды.
Перед перезапуском Nginx обязательна проверка синтаксиса.
$ sudo nginx -t
Если конфиг битый, перезапуск приведёт к падению веб-сервера и недоступности всех сайтов на нём - привычка проверять конфиг перед reload спасала уже тысячи раз.
$ sudo systemctl restart nginx
После этого по адресу https://sails.example.com открывается работающее приложение с полноценным шифрованием. Браузер показывает зелёный замочек, сертификат валидный, HTTP/2 активен.
Регистрация пользователя в развёрнутом приложении и итоговые соображения о выборе фреймворка для серверной разработки
Открыв адрес сайта, посетитель видит готовую главную страницу шаблона Sails Web App. В правом верхнем углу присутствует кнопка Sign up для создания учётной записи. Клик ведёт на форму регистрации, где запрашивается имя, email и пароль. После успешного заполнения система автоматически авторизует свежеиспечённого пользователя и переносит его на welcome-страницу.
С этого момента приложение готово принимать пользователей и обрабатывать их запросы. В стандартный шаблон встроена поддержка Stripe для приёма платежей, система восстановления пароля через email, базовая работа с профилями. Дальнейшее развитие зависит только от задачи - можно добавлять модели через sails generate, описывать ассоциации между ними, создавать REST-эндпоинты или WebSocket-подписки.
Sails.js не самый популярный фреймворк в JavaScript-экосистеме, и тем кто привык к Express может показаться громоздким. Но в нишах, где важна структура и предсказуемость, он раскрывается во всей красе. Корпоративные приложения, внутренние сервисы, системы с реалтайм-коммуникациями - вот где Sails чувствует себя уверенно. Конвенции вместо конфигурации, ORM из коробки, готовые хуки для типовых задач - всё это позволяет сократить время от идеи до прототипа до считанных дней.
Связка Sails + Nginx + Let's Encrypt + systemd, которая получилась в итоге, представляет собой типовую боевую конфигурацию для production-развёртывания Node-приложения. Шаблон масштабируется вертикально через увеличение ресурсов сервера, горизонтально - через добавление нескольких бэкендов в upstream-блок Nginx и подключение балансировщика. Принципы, описанные здесь, применимы практически к любому Node-фреймворку - меняется только команда запуска в systemd-юните и порт, который слушает приложение. Освоение этой схемы один раз окупается многократно при каждом следующем проекте.